핵심 정리
console.log("A");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("promise"));
queueMicrotask(() => console.log("microtask"));
console.log("B");여기서 기본 감각은 이겁니다.
- 현재 동기 코드가 먼저 끝난다
- 그 직후 microtask를 비운다
- 그다음 timers나 I/O 계열이 온다
실행 순서
이벤트 루프를 다시 읽을 때 먼저 붙잡으면 좋은 기본 축은 아래입니다.
- 현재 동기 코드
process.nextTick- Promise /
queueMicrotask - timer (
setTimeout) - I/O 이후
setImmediate
Promise / queueMicrotask / nextTick
이 셋은 "지금 콜백이 끝난 직후"와 관련 있습니다. 일반적으로 Promise와 queueMicrotask는 timers보다 먼저 보이고, process.nextTick은 더 강하게 앞서는 편입니다.
핵심은 "이벤트 루프 다음 바퀴"보다 "지금 바퀴가 끝나기 전 정리 작업"에 가깝다는 점입니다.
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("promise"));
process.nextTick(() => console.log("nextTick"));보통은 nextTick -> Promise/queueMicrotask -> timer 순서 감각으로 읽는 편이 빠릅니다.
즉 nextTick은 "조금 뒤"라기보다 "이벤트 루프가 다른 일로 넘어가기 전에 먼저 비워지는 큐"로 보는 편이 맞습니다.
setTimeout(0) vs setImmediate
둘은 둘 다 "나중"이지만 질문이 다릅니다.
setTimeout(0): timers 단계setImmediate: check 단계
특히 I/O 콜백 안에서는 setImmediate가 먼저 보이는 경우가 많습니다.
import { readFile } from "node:fs";
readFile("./data.txt", () => {
setTimeout(() => console.log("timeout"), 0);
setImmediate(() => console.log("immediate"));
});이 경우에는 setImmediate가 먼저 보일 수 있습니다. 그래서 "무조건 timeout이 먼저"로 외우기보다 "지금 I/O 콜백 안인가"를 같이 봐야 합니다.
CPU 작업
이벤트 루프를 막는 것은 결국 동기 CPU 작업
function dangerousSum(n) {
let total = 0;
for (let i = 0; i < n; i++) total += i;
return total;
}이런 코드는 await가 없어도 "한 스레드 전체"를 오래 붙잡습니다. 그래서 Node에서 이벤트 루프 카드는 곧 CPU 바운드 작업 경계 카드이기도 합니다.
대응은 보통 이 셋 중 하나입니다.
- 작업을 청크로 나누기
setImmediate같은 지점으로 제어권 양보- 진짜 CPU 바운드면
worker_threads
순서를 외우기보다 "지금 어느 경계 안인가"를 본다
이벤트 루프 카드는 전체 phase 이름을 외우는 카드보다, 현재 동기 실행 안인지, I/O 콜백 안인지, microtask를 계속 쌓고 있는지 판단하는 카드에 가깝습니다.
무엇이 먼저 도나
체크포인트
- 동기 코드가 먼저 끝난다
- microtask가 timers보다 앞선다
- I/O 이후
setImmediate가 유리한 경우가 많다 - CPU 바운드 작업은 이벤트 루프 전체를 막는다
nextTick은 너무 많이 쌓으면 다른 작업을 굶길 수 있다
주의할 점
process.nextTick이나 Promise 체인을 재귀적으로 계속 이어 붙이면 "비동기"처럼 보여도 실제로는 이벤트 루프가 다른 작업으로 넘어갈 틈을 잃습니다. 순서 제어용으로 조금 쓰는 것과, 루프를 굶기는 것은 완전히 다른 문제입니다.
참고 링크
2 sources