Event Loop 다시잡기
이전에 간단하게 접해봤던 event loop에 관련되어서 테스트를 해보던 중, 잘못알고 있었거나 부족했던 점이 있어 추가적으로 정리해보려고 한다.
Event Loop
자바스크립트언어는 하나의 작업영역을 가지고있는 single thread로서, stack형식의 자료구조를 갖고 있다.
각각의 작업들은 stack에 차곡차곡 쌓여가며, 선입 후출의 방식으로 일이 진행된다.
그러다보니 A 작업이 시작된다면 B 작업은 중간에 끼어들 수 없는 형식이다.
Run-to-completion 이라고 함. 내 생각에는, 이러한 방식이 동기 적인 프로그래밍이 아닐까 싶음
일반적인 함수와 같은 작업영역의 경우 크게 문제되지 않겠지만, 시간이 오래걸리는 작업의 경우 사용자에게 부정적인 경험을 줄 수 있다.
일반적인 경우는, 이전의
event loop포스트에서 다뤘으니 패스
Web API
Web API에는 setTimeout이나, Promise등이 포함되는 브라우저에서 제공하는 API를 의미한다.
중요한것은, setTimeout이나, Promise, 각각의 이벤트에 바인딩되는 콜백함수들이다.
Que
Web API의 비동기함수들은 자신의 콜백함수들을 종류에 따라, 각각의 Que 자료구조에 담기게 된다.
-
MicroTasks:Promise객체를 통한 콜백함수들이 담기는 곳.then/catch/finally와 같은 비동기 메소드들의 내부는Promise가 즉시실행함수로 실행되었다 하더라도,MicroTasks로 넘어감 Tasks:UI이벤트 혹은setTimeout의 콜백함수들이 담기는 곳.
Promise와 같은 비동기 API들은, 현재 stack에서 진행중인 작업이 완료되자마자 실행되어야 하기 때문에 Microtasks가 Tasks보다 높은 우선순위에 있다고 한다.
현재의 stack이 마지막 작업을 수행 완료 하였을 때 를 가정해본다면,
Event Loop는MicroTasks의 상황을 확인함MicroTasks에 쌓여있는 작업이 있다면, 선입선출의 방식으로 가져감MicroTasks에 쌓여있는 작업이 없다면,Tasks에서 선입선출의 방식으로 가져감
예제1
function microTaskDone(num) {
console.log(`MicroTask done`)
}
function taskDone() {
console.log('Task done')
}
function done() {
console.log('Start Done')
}
function start() {
Promise.resolve().then(microTaskDone)
setTimeout(() => {
taskDone()
})
done()
}
start()
console.log('main done')
// 결과
Stack Done
main done
MicroTask done
Task donestart스택이 쌓임Promise.resolce().then()은Web API비동기 영역으로, 콜백함수는MicroTasks에 저장된 다음, 해당 스택은 사라짐setTimeout은Web API비동기 영역으로, 이후의 콜백함수는Tasks에 저장된 다음, 해당 스택은 사라짐done스택이 쌓이고,Start Done작업을 완료하고 해당 스택은 사라짐start스택이 사라지고,main done작업을 완료함Stack이 마지막 작업을 완료함을 감지한Event Loop는MicroTasks를 확인하고,microTaskDone작업이 존재하여Stack으로 작업을 올림.MicroTask done작업을 완료하고, 해당 스택은 사라짐.Event Loop는 다시한번Stack이 마지막 작업이 완료됨을 감지하고MicroTasks를 확인하지만, 아무런 작업도 존재하지 않아Tasks의 첫번째 작업을Stack으로 올림Task done작업을 완료하고 해당 스택은 사라짐.
예제2
예제2의 경우 조금 다른 결과라 맞는 해석일지는 잘 모르겠지만, Stack의 작업이 종료될때마다 새로운 작업을 찾아서 올리는 Event Loop의 특성과, 즉시실행이아닌 어느정도 시간이 걸리는 비동기 Web API들을 고려해보면 맞을것 같음.
function promise() {
return new Promise((res, rej) => {
fetch('./json/json1.json')
.then(json => {
console.log("fetch의 비동기 then1");
return json.json();
})
.then(() => {
console.log("fetch의 비동기 then2");
return res();
});
})
}
function microTaskDone(num) {
console.log(`MicroTask done`)
}
function task() {
setTimeout(() => {
console.log('Task done')
})
}
function done() {
console.log('Stack Done')
}
function start() {
promise().then(microTaskDone)
task()
done()
}
start()
console.log('main done')
// 결과
Stack Done
main done
Task done
fetch의 비동기 then1
fetch의 비동기 then2
MicroTask donestart스택이 쌓임-
Promise.resolce().then()은Web API비동기 영역으로, 콜백함수는MicroTasks에 저장된 다음, 해당 스택은 사라짐단 여기서 콜백함수는
fetch메소드를 의미함 setTimeout은Web API비동기 영역으로, 이후의 콜백함수는Tasks에 저장된 다음, 해당 스택은 사라짐done스택이 쌓이고,Start Done작업을 완료하고 해당 스택은 사라짐start스택이 사라지고,main done작업을 완료함Stack이 마지막 작업을 완료함을 감지한Event Loop는MicroTasks를 확인하고,fetch작업이 존재하여Stack으로 작업을 올림.Stack에 올라간fetch또한,Promise객체를 반환하는 비동기Web API로,MicroTasks에 넘겨주면서Stack의fetch는 사라짐.-
Event Loop는 다시한번Stack이 마지막 작업이 완료됨을 감지하고MicroTasks를 확인하지만, 아직은 아무런 작업도 존재하지 않아Tasks의 첫번째 작업을Stack으로 올림fetch라는 비동기Web API는 내부적으로 바로 실행되는것이 아닌, 어느정도 시간이 필요하기 때문에, 바로 콜백함수를 넘겨주진 못하는것 같음 Task done작업을 완료하고 해당 스택은 사라짐.Web API에서 진행중이던fetch가 완료되고,.then의 콜백함수가MicroTasks에 저장됨.Task done작업이 사라지고,Event Loop는 다시 반복함.