해당 포스트는
JavaScript
의 이벤트 루프의 동작에 대해 정리한 포스트입니다.
주관적인 생각을 정리해서 잘못 설명된 부분이 있을 수 있습니다.
사실 이 글을 읽는 것보다는 최하단의 레퍼런스를 보는 것이 더 도움됩니다.
( 이해를 도와주는 시각적인 자료가 많음 )
✍️ 용어 정리
0️⃣ 태스크 ( task )
작업의 단위
1️⃣ 매크로태스크
event
, setTimeOut()
같은 비동기의 작업 단위입니다.
2️⃣ 마이크로태스크
promise
의 .then/catch/finally
핸들러들의 작업 단위입니다.
3️⃣ 큐 ( queue )
자료구조 중 하나로 선입선출이 특징입니다.
4️⃣ 스택 ( stack )
자료구조 중 하나로 후입선출이 특징입니다.
📌 마이크로태스크와 마이크로태스크 큐 ( microtask )
마이크로태스크란 promise
의 .then/catch/finally
핸들러들의 작업을 의미합니다.
마이크로태스크 큐란 마이크로태스크가 완료된 후 실행 대기 상태에 보관되는 큐(queue
)입니다.
📌 매크로태스크와 매크로태스크 큐 ( macrotask )
매크로태스크란 setTimeOut
, event
등의 작업을 의미합니다.
setTimeOut
이 실행되고 난 후 시간과 콜백 함수로 매크로태스크를 만들고 시간이 지난 실행 대기 상태를 매크로태스크 큐에 보관합니다.
event
가 실행되고 핸들러에 의해 처리할 콜백 함수를 매크로태크스 큐에 보관합니다.
0️⃣ 매크로태스크 예시
<script src="">
- 마우스 움직임 (
mousemove
이벤트와 핸들러 ) setTimeout
의 콜백 함수 실행alert()
➰ 이벤트 루프
태스크가 들어오길 기다렸다가 태스크가 들어오면 이를 처리하고, 처리할 태스크가 없는 경우엔 잠드는 동작을 끊임없이 반복하는 JavaScript
내 루프입니다.
0️⃣ 이벤트 루프의 동작
- 매크로태스크 큐 확인
- 매크로태스크 큐가 비어있지 않다면 가장 먼저 들어온 태스크를
call stack
으로 이동 - 마이크로태스크 큐 확인
- 마이크로태스크 큐가 비어있지 않다면 마이크로태스크 큐의 모든 태스크들을 순차적으로
call stack
으로 이동 - 랜더링할 것이 있으면 랜더링
- 마이크로태스크 큐와 매크로태스크 큐가 모두 비어있다면 잠들고 그게 아니라면 1번부터 다시 수행 ( 태스크가 추가되면 깨어남 )
즉, 요약하면 마이크로태스크의 대기중인 태스크는 한번에 모두 순차적으로 실행되고 매크로태스크는 순차적으로 하나씩 실행하되 마이크로태스크가 비어있는지 검사를 합니다.
이렇게 동작하는 이유는 마이크로태스크의 내용이 이벤트나 네트워크 요청 등의 환경에 변화는 주는 요인에 영향을 받지 않고 같은 환경에서 실행되게 하기 위함이라고 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// (1) 매크로태스크
setTimeout(() => {
console.log("타이머");
}, 0);
// (2) 마이크로태스크
Promise.resolve("프로미스")
.then((res) => {
console.log(res);
return res + "2";
})
.then(console.log);
// (3) 바로 call stack
console.log("일반");
/**
* "일반"
* "프로미스"
* "프로미스2"
* "타이머"
*/
setTimeout()
((1)
)을call stack
에 담고 실행되며 타이머(0
)와 콜백 함수를 매크로태스크로 생성(?) ( 이후 타이머 계산 후 조건에 만족하면 매크로태스크 큐로 이동 )promise
((2)
)를call stack
에 담고 실행되며.then()
을 만나면 콜백 함수를 마이크로태스크로 생성(?) ( 이후 준비가 완료되면 마이크로태스크 큐로 이동 )(2)
의.then()
이 준비 완료라면 다음.then()
을 2번처럼 처리 ( 끝까지 반복 )(3)
을call stack
에 담고 실행 ( 마이크로태스크/매크로태스크 큐에 대기중이라도call stack
이 비어있지 않으면 실행하지 않음 )- 모든 코드가 종료됐기 때문에
call stack
이 비워짐 - 우선순위에 따라서 마이크로태스크 큐의 내용들이 먼저
call stack
으로 순차적으로 이동 후 실행 - 매크로태스크 큐의 내용이
call stack
으로 순차적으로 이동 후 실행 - 7이 끝나면 다시 6을 확인 후 있다면 6 없다면 7 실행
- 매크로태스크 큐가 끝날 때까지 반복
call stack
과 마이크로태스크/매크로태스크 큐가 모두 비었다면 이벤트 루프 수면 ( 다시 태스크가 들어오면 깨어나서 6 ~ 9를 반복 )
💡 Tip
특정 태스크를 처리하는 동안에는 렌더링이 절대 일어나지 않습니다.
태스크 처리에 긴 시간이 걸린다면 그 동안 모든 이벤트의 실행이 대기됩니다.
setTimeOut(callback, 0)
를 사용하면 함수를 매크로태스크 큐에 넣을 수 있습니다.
queueMicrotask(func)
를 사용하면 함수를 마이크로태스크 큐에 넣을 수 있습니다.