자바스크립트 퀴즈북 리마인드 Day 8: 이벤트 루프와.
콜 스택, 태스크 큐, 마이크로태스크 큐를 기준으로 Promise와 setTimeout 실행 순서를 코드 리뷰 관점에서 정리합니다.
오늘의 질문: “setTimeout(..., 0)은 정말 바로 실행될까?”console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
Promise.resolve()
.then(() => {
console.log("C");
})
.then(() => {
console.log("D");
});
console.log("E");출력은 A E C D B입니다. setTimeout(..., 0)이 “0초 뒤 즉시 실행”이라는 뜻으로 보이면 B를 너무 앞에 놓기 쉽습니다. 실제로는 현재 동기 코드가 끝나고, 마이크로태스크 큐가 먼저 비워진 뒤, 다음 태스크에서 timeout 콜백이 실행됩니다.
비동기 코드를 “시간이 짧은 순서대로 실행된다”고 생각하면 Promise와 timer가 섞인 버그를 잘못 읽습니다. 브라우저와 Node 런타임은 실행 순서를 단순한 시간표가 아니라 스택이 비는 시점과 큐의 우선순위로 정합니다.
리뷰할 때는 이 정도만 기억해도 대부분의 순서를 설명할 수 있습니다.
지금 실행 중인 동기 코드는 call stack에서 끝까지 돈다. 스택이 비면 microtask queue를 비운다. microtask 안에서 새 microtask가 생기면 그것도 같은 턴에 이어서 돈다. 그 뒤 렌더 기회가 오고, 다음 task가 하나 실행된다. 다음 task가 끝나면 다시 microtask를 비운다.
그래서 Promise.then, queueMicrotask, await 뒤쪽 코드는 보통 setTimeout보다 먼저 보입니다.
setTimeout(() => console.log("task"), 0);
queueMicrotask(() => console.log("microtask"));
console.log("sync");
// sync
// microtask
// taskawait가 있으면 함수가 잠깐 멈추는 것처럼 보입니다. 하지만 “스레드가 멈춘다”보다 “나머지 부분이 Promise continuation으로 예약된다”에 가깝게 보는 편이 안전합니다.
async function run() {
console.log("1");
await null;
console.log("2");
}
run();
console.log("3");
// 1
// 3
// 2await null은 즉시 끝나는 값처럼 보여도, console.log("2")는 현재 동기 흐름 뒤로 밀립니다. UI 코드에서 await 뒤에 상태를 쓰는 부분은 “이미 다른 이벤트나 렌더가 끼어들 수 있었나?”까지 같이 봐야 합니다.
| 코드 냄새 | 왜 위험한가 | 리뷰 질문 |
|---|---|---|
setTimeout(fn, 0)으로 순서 보정 | 다음 task로 미룰 뿐, 안정적인 상태 동기화가 아니다 | 왜 다음 턴이어야 하는가? |
| Promise chain 안에서 DOM/state 대량 갱신 | microtask가 길면 렌더 기회가 늦어진다 | 한 번에 너무 많은 일을 밀어 넣지 않았는가? |
await 뒤에 오래된 값 사용 | 그 사이 다른 작업이 끝났을 수 있다 | 취소/최신성 검사를 했는가? |
| 이벤트 핸들러 안에서 microtask 재귀 | 렌더나 다음 이벤트가 밀릴 수 있다 | 루프가 큐를 독점하지 않는가? |
이벤트 루프는 “외울 순서”가 아니라 리뷰할 때 버그의 시간 축을 그리는 도구입니다. 출력 순서 문제, 로딩 상태 깜빡임, 클릭 직후 DOM 측정, 오래된 async 결과 덮어쓰기 모두 같은 질문으로 시작할 수 있습니다.
이 콜백은 어느 큐에 들어가고, 그 전에 어떤 큐가 먼저 비워질까?
'console.log("A");\nsetTimeout(() => console.log("B"), 0);\nPromise.resolve().then(() => console.log("C")).then(() => console.log("D"));\nconsole.log("E");'Post Q&A
자바스크립트 퀴즈북 리마인드 Day 8: 이벤트 루프와 Promise 타이밍 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!
클로저가 값 복사가 아니라 렉시컬 환경 참조라는 점을 stale closure, 루프 콜백, React 이벤트 리뷰 관점에서 정리합니다.