마이크로태스크란
- 지금 실행 중인 코드가 끝나자마자, 브라우저가 다른 일(렌더링·다음 이벤트)을 하기 전에 먼저 처리되는 짧은 작업 큐
- 만드는 것: Promise
.then/.catch/.finally,await이후 코드,queueMicrotask(),MutationObserver
이벤트 루프 순서
① 매크로태스크 하나 실행 (예: 현재 스크립트)
② 마이크로태스크 큐를 전부 비움
③ (필요하면) 렌더링
④ 다음 매크로태스크 하나 실행
→ 반복
"매크로 하나 → 마이크로 전부 → 렌더 → 매크로 하나" 리듬이다.
매크로 vs 마이크로
| 매크로태스크 | 마이크로태스크 | |
|---|---|---|
| 예시 | setTimeout, setInterval, 이벤트 핸들러, I/O | Promise 콜백, await 이후, queueMicrotask |
| 실행 | 한 번에 하나씩 | 현재 코드 직후 큐를 몽땅 |
| 렌더 전 처리 | 아니오 | 예 (렌더보다 먼저 다 비움) |
출력 순서
console.log("1");
setTimeout(() => console.log("2"), 0); // 매크로
Promise.resolve().then(() => console.log("3")); // 마이크로
console.log("4");
// 출력: 1 → 4 → 3 → 2
동기 코드(1, 4) → 마이크로 비움(3) → 다음 매크로(2). setTimeout(0)이라도 마이크로가 항상 먼저다.
await는 마이크로태스크다
async function f() {
console.log("a");
await something; // 여기서 함수가 잠깐 멈춤
console.log("b"); // await 이후는 마이크로태스크로 예약됨
}
await는 문법 설탕이고, 뒷부분(b)이 .then 콜백처럼 마이크로태스크 큐에 들어가 실행된다.
함정 — 마이크로태스크 기아(starvation)
마이크로 큐는 전부 비울 때까지 다음 단계로 안 넘어간다. 마이크로 안에서 또 마이크로를 계속 만들면 렌더링·매크로가 실행되지 못한다.
function loop() {
Promise.resolve().then(loop); // 무한히 마이크로 재생성
}
loop();
// → 렌더링·setTimeout이 영영 실행 못 됨 (브라우저 멈춤)
무거운 반복 작업은 매크로(setTimeout)로 쪼개야 한다.
queueMicrotask
- "동기 코드 직후, 렌더 전에" 실행하고 싶을 때. Promise를 만들지 않고 마이크로태스크만 예약하는 방법
queueMicrotask(() => console.log("동기 코드 직후 실행"));
정리
- 마이크로태스크 = Promise/await가 만드는, 현재 코드 직후 렌더보다 먼저 몰아서 처리되는 우선 큐
- 매크로태스크(
setTimeout등)보다 항상 먼저이고, 큐를 완전히 비운 뒤에야 다음 단계로 넘어간다