Event Loop의 개념은 HTML 스펙에 잘 정리되어 있습니다. 문서에서 Event Loop, Task Queue의 개념에 대해 잘 정의되어 있는 것을 볼 수 있을 것입니다. 그런데 문서 중간에 MicroTask Queue(마이크로 태스크 큐)
라는 생소한 용어가 존재합니다.
setTimeout(function() => {
console.log('A');
}, 0);
Promise.resolve().then(() => {
console.log('B');
}).then(() => {
console.log('C');
});
콘솔에 찍히는 순서는 어떻게 될까요? Promise
도 비동기로 실행된다고 할 수 있으니 Task Queue에 추가돼서 순서대로 A → B → C 가 될까요? 아니면 Promise는 setTimeout 처럼 최소단위 지연이 없으니 B → C → A 일까요? 체인 형태로 연속해서 호출된 then()
함수는 어떤 식으로 동작할까요?
결론부터 말하자면 정답은 B → C → A 입니다. 이유는 바로 프로미스가 MicroTask Queue
를 사용하기 때문입니다. 그럼 MicroTask Queue
가 대체 무엇인가?
*MicroTask Queue
는 쉽게 말해 일반 태스크 보다 더 높은 우선순위를 갖는 태스크라고 할 수 있습니다. 즉, Task Queue에 대기중인 태스크가 있더라도 마이크로 태스크가 먼저 실행됩니다. 위의 예제를 통해 좀더 자세히 알아보겠습니다.*
*setTimeout()
함수는 콜백 A를 Task Queue에 추가하고, Promise의 then()
메소드는 콜백 B를 Task Queue가 아닌 별도의 MicroTask Queue
에 추가합니다.*
위의 코드의 실행이 끝나면 태스크 Event Loop는 Task Queue 대신 MicroTask Queue
가 비었는지 먼저 확인하고, 해당 큐에 있는 콜백 B를 실행합니다. 콜백 B가 실행되고 나면 두번째 then()
메소드가 콜백 C를 MicroTask Queue
에 추가합니다. Event Loop는 다시 MicroTask Queue
가 비었는지 확인하고, 큐에 있는 콜백 C를 실행합니다. 이후에 MicroTask Queue
가 비었음을 확인한 다음 Task Queue에 콜백 A를 꺼내와 실행합니다.
잘 와 닿지 않는 분들은 이와 관련해서 인터랙션과 함께 아주 잘 정리된 글이 있습니다. (참고)
원문 글에서는 브라우저마다 Promise의 호출 순서가 다른 문제를 지적하고 있는데, 이유는 Promise
가 ECMAScript
에 정의되어 있는 반면, MicroTask
는 HTML 스펙
에 정의되어 있는데, 둘의 연관관계가 명확하지 않기 때문이다.
(ECMAScript에는 ES6 부터 Promise를 위해 잡 큐(Job Queue)라는 항목이 추가되었지만, HTML 스펙의 MicroTask 와는 별도의 개념이다.)