1. 마이크로태스크
Promise 핸들러인 .then/catch/finally는 항상 비동기적으로 실행된다.
--> Promise가 즉시 이행되더라도 .then/catch/finally 아래 있는 코드는 이 핸들러들이 실행되기 전에 실행된다.
let promise = Promise.resolve();
promise.then(() => alert("프라미스 성공!"));
alert("코드 종료");
//"코드 종료" 라는 얼럿창이 가장 먼저 뜨고, "프라미스 성공!"이 나중에 출력된다.
Promise가 즉시 이행상태가되고나서 .then을 실행했는데 왜 순서가 바꾸었을까?
2. 마이크로태스크 큐
비동기 작업을 처리하려면 적절한 관리가 필요한다. 이를 위해 ECMA에서는 PromiseJobs라는 내부 큐(internal queue)를 명시한다.
V8엔진에서는 이것을 '마이크로태크스 큐(microtask queue)'라고 부른다.
- 마이크로태스크 큐는 먼저 들어온 작업을 먼저 실행한다. (FIFO, first-in-first-out)
- 실행할 것이 아무것도 남아있지 않을 때만 마이크로태스크 큐에 있는 작업이 실행되기 시작한다.
--> 어떤Promise가 준비되었을 때 이 Promise의 .then/catch/finally 핸들러가 큐에 들어가는 것!! 이때 핸들러들은 여전히 실행되지 않고 현재 코드에서 자유로운 상태가 되었을 때에서야 자바스크립트 엔진은 큐에서 작업을 꺼내 실행한다.
--> Promise핸들러는 항상 내부 큐를 통과하게 된다. 여러 개의 .then/catch/finally를 사용해 만든 체인의 경우, 각 핸들러는 비동기적으로 실행되는데, 큐에 들어간 핸들러 각각은 현재 코드가 완료되고, 큐에 적채된 이전 핸들러의 실행이완료되었을 때 실행되는 것이다.
((위의 예시를 이해하기 위한 참고 자료))
--> 마이크로 태스크 vs 매크로 태스크
- 마이크로 태스크 : 마이크로태스크들은 실행하면서 새로운 마이크로 태스크를 큐에 추가할 수도 있다. 새롭게 추가된 마이크로태스크도 큐가 빌 때까지 계속해서 실행된다.
(process.nextTick, Promises, queueMicrotask(f), MutationObserver) - 이벤트 루프는 매크로 태스크 큐에 있는 것을 실행시키기 시작할 때 있는 매크로 태스크만 실행시킨다.매크로 태스크가 추가한 매크로 태스크는 다음 이벤트 루프가 실행될 때까지 실행되지 않는다.
(requestAnimationFrame, I/O, UI rendering, setTimeout, setInterval, setImmediate)
-> 이벤트 루프의 반복
- 매크로 태스크 큐에서 가장 오래된 태스크를 꺼내서 실행시킨다.
- 마이크로 태스크 큐에 있는 모든 태스크를 실행시킨다.
- 랜더링 작업을 실행한다.
- 매크로 태스크 큐에 새로운 매크로 테스크가 나타날 때까지 대기한다.
- 1번으로 돌아간다.
2-1. Example
console.log('script start'); // A
setTimeout(function () { // B
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(function () { // C
console.log('promise1');
})
.then(function () { // D
console.log('promise2');
});
console.log('script end'); // E
//script start
//script end
//promise1
//promise2
//setTimeout
위 코드의 실행과정
1. 콜 스택에는 전역 실행 객체가 있고, '스크립트 실행'이라는 태스크가 매크로 태스크 큐에 들어있다.
2. 이벤트 루프는 매크로 태스크 큐에 있는 '스크립트 실행' 태스크를 실행한다.
3. A에 도달하면, 'script start'가 출력된다.
4. B에 도달하면, setTimeout web api가 타이머를 실행시키고, 타이머가 종료되면 콜백 함수가 매크로 태스크 큐에 들어간다.
5. C에 도달하면, 콜백 함수가 마이크로 태스크 큐에 들어간다.
6. E에 도달하면, 'script end'가 출력된다.
7. 콜 스택이 비었으므로, 이벤트 루프는 마이크로 태스크 큐에 있는 프라미스 콜백 함수를 실행시킨다.
8. 'promise 1'이 출력된다.
9. Promise.then 메서드는 D 콜백 함수를 마이크로 태스크 큐에 등록한다.
10. 이벤트 루프는 다음 마이크로 태스크인 D 콜백 함수가 실행시킨다.
11. 'promise 2'가 출력된다.
12. 렌더링할 것이 있으면, 브라우저는 렌더링을 한다.
13. 매크로 태스크 큐에 있는 setTimeout 콜백함수를 실행시킨다.
14. 'setTimeout'이 출력된다.
'Front-end > JavaScript' 카테고리의 다른 글
[JavaScript] 제너레이터 (0) | 2022.05.31 |
---|---|
[JavaScript] async와 await (0) | 2022.05.30 |
[JavaScript] 프라미스화 Promisfication (0) | 2022.05.29 |
[JavaScript] this & argument가 없는 화살표 함수 (0) | 2022.05.09 |
[JavaScript] 함수 바인딩 (bind) (0) | 2022.05.09 |