setTimeout vs Promise.then vs queueMicrotask

2025. 9. 5. 10:37·Frontend Development
728x90

브라우저/Node.js 런타임에서 비동기 코드의 실행 순서를 정확히 이해하는 것은 디버깅과 성능 최적화의 출발점입니다. 특히 마이크로태스크 큐는 렌더링 타이밍과 상태 일관성에 직접적인 영향을 주므로, 실무에서 정확한 모델을 갖추는 것이 중요합니다.


핵심 개념 정리

  • Call Stack: 자바스크립트가 함수를 동기적으로 실행하는 스택.
  • Web APIs(환경): 타이머, DOM, Fetch 등 비동기 작업이 대기하는 영역.
  • Task Queue(매크로태스크 큐): setTimeout, setInterval, setImmediate(Node) 등이 들어가는 큐.
  • Microtask Queue(마이크로태스크 큐): Promise.then/catch/finally, queueMicrotask, MutationObserver 등이 들어가는 큐.
  • Event Loop 규칙(중요):
    1) 콜스택이 빌 때까지 동기 코드 실행
    2) Microtask Queue를 “완전히” 비울 때까지 실행
    3) 그 다음 Task Queue에서 작업 1개를 실행(그리고 다시 2로)

요약하면, “마이크로태스크는 같은 틱에서 모두 비워진 뒤에야 다음 매크로태스크로 넘어간다”가 핵심입니다.


예제 코드

아래 코드는 test.js와 동일합니다.

setTimeout(() => {
    console.log('A');
}, 0);

console.log('B');

Promise.resolve().then(() => {
    console.log('C');
});

queueMicrotask(() => {
    console.log('D');
});

console.log('E');

기대 출력(브라우저 기준)

B
E
C
D
A

단계별 실행 타임라인

1) setTimeout(..., 0) 등록 → Web APIs로 타이머 위임 → 만료 후 Task Queue에 콜백(A) 대기

2) 동기 코드 실행: console.log('B')

3) Promise.resolve().then(...) → Microtask Queue에 콜백(C) 등록

4) queueMicrotask(...) → Microtask Queue에 콜백(D) 등록

5) 동기 코드 실행: console.log('E')

6) 콜스택이 비면 Event Loop가 Microtask Queue를 “모두” 비움 → C → D

7) 다음 틱에서 Task Queue의 A 실행 → console.log('A')

정리: 동기(B, E) → 마이크로태스크(C, D) → 매크로태스크(A)

마이크로태스크 내부 순서는 “등록된 순서”입니다. 위 코드에서는 Promise.then이 먼저, queueMicrotask가 나중에 등록되어 C → D 순으로 실행됩니다.


비교 표: 어떤 큐로 가는가, 언제 실행되는가

항목 setTimeout Promise.then queueMicrotask
큐 유형 Task(매크로태스크) Microtask Microtask
실행 시점 다음 틱 이후 현재 틱의 콜스택 종료 직후 현재 틱의 콜스택 종료 직후
순서 보장 동일 지연이면 상대적 FIFO(등록 순) FIFO(등록 순)
렌더링 영향 보통 렌더 후 실행 렌더 전 마이크로태스크가 모두 비워짐 렌더 전 마이크로태스크가 모두 비워짐
에러 전파 타이머 콜백에서 throw 시 비동기 에러 잡히지 않으면 전역 Unhandled Rejection 잡히지 않으면 전역 에러
취소 clearTimeout 불가(체인 취소는 별도 로직) 불가
대표 용도 지연 실행, 렌더 이후 작업 비동기 후크/체인 매우 짧은 후처리, 동 틱 내 정합 보장

실무 팁과 주의사항

  • 마이크로태스크 “고갈” 규칙을 이용해 일관성 보장
    • 동일 틱 내 후처리는 queueMicrotask가 가장 명확합니다.
  • 과도한 마이크로태스크 생성은 이벤트 루프 기아를 유발
    • 무한 재귀적 마이크로태스크는 렌더링을 막습니다. 반드시 종료 조건을 두세요.
  • Promise.then vs queueMicrotask
    • 둘 다 마이크로태스크이지만, 체인 작성과 에러 핸들링이 필요하면 Promise가 편리합니다. 아주 경량 후처리는 queueMicrotask가 적합합니다.
  • 브라우저 vs Node.js 차이 인지
    • Node.js에서는 process.nextTick이 마이크로태스크보다 우선 실행되어 기아를 유발하기 쉽습니다. 범용적으로는 queueMicrotask/setImmediate 사용을 권장합니다.
  • 테스트에서 플러시하기
    • 마이크로태스크만 비우고 싶다면 await Promise.resolve()로 한 틱을 양보하는 패턴을 사용할 수 있습니다.

변형 실험으로 개념 고정

1) 타이머 내부의 마이크로태스크는 “다음 매크로태스크 안”에서 먼저 비워집니다.

setTimeout(() => {
    console.log('A1');
    queueMicrotask(() => console.log('A2')); // 같은 틱에서 A2가 A1 바로 뒤에 실행
}, 0);

console.log('B1');

예상: B1 → (다음 틱) A1 → A2

2) 등록 순서가 곧 마이크로태스크 실행 순서입니다.

queueMicrotask(() => console.log('M1'));
Promise.resolve().then(() => console.log('M2'));
queueMicrotask(() => console.log('M3'));

예상: M1 → M2 → M3


결론

  • 상태 정합이 중요한 “동 틱 내 후처리”는 queueMicrotask로, 체인/에러 전파가 필요하면 Promise로.
  • 렌더 이후로 미루고 싶다면 setTimeout(0)이 아니라, 의도에 맞는 API(requestAnimationFrame, requestIdleCallback)를 고려하세요.
  • Node.js에서는 process.nextTick 남용을 지양하고 queueMicrotask/setImmediate를 선택하세요.

핵심 공식: 동기(B/E) → 마이크로태스크(C/D 전체 소진) → 매크로태스크(A). 이 규칙으로 대부분의 실행 순서를 안정적으로 예측할 수 있습니다.

728x90
저작자표시 비영리 변경금지 (새창열림)

'Frontend Development' 카테고리의 다른 글

useEffect에서 setInterval이 상태를 못 따라오는 이유 (stale closure)  (0) 2025.09.07
React Error Boundary: 왜 아직도 클래스일까?  (0) 2025.09.07
Next.js 최신 캐싱 전략 총정리  (0) 2025.09.03
면접에서 묻는 "의존성 주입 경험이 있나요?"의 의미  (1) 2025.08.26
useState vs useRef vs let: 언제 무엇을 써야 할까?  (0) 2025.08.21
'Frontend Development' 카테고리의 다른 글
  • useEffect에서 setInterval이 상태를 못 따라오는 이유 (stale closure)
  • React Error Boundary: 왜 아직도 클래스일까?
  • Next.js 최신 캐싱 전략 총정리
  • 면접에서 묻는 "의존성 주입 경험이 있나요?"의 의미
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (133)
      • Frontend Development (62)
      • Backend Development (25)
      • Algorithm (33)
        • 백준 (11)
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
      • Language (6)
        • JavaScript (6)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    frontend development
    tailwindcss
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
setTimeout vs Promise.then vs queueMicrotask
상단으로

티스토리툴바