JavaScript는 단일 스레드에서 실행되므로 한 번에 한 가지 일만 할 수 있습니다. **이벤트 루프(event loop)**는 블로킹 없이 비동기 작업을 처리할 수 있게 해줍니다: 콜 스택을 실행하고, 스택이 비면 큐에서 콜백을 꺼냅니다.
우선순위가 다른 두 개의 큐가 있습니다:
- 마이크로태스크(Microtasks): Promise 콜백(
.then),queueMicrotask. 각 태스크 이후, 렌더링 전에 완전히 비워집니다. - 매크로태스크(Macrotasks):
setTimeout, I/O, UI 이벤트. 루프 반복마다 하나씩 처리됩니다.
js
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.
그래서 둘 다 "비동기"임에도 Promise.then은 항상 setTimeout(…, 0)보다 먼저 실행됩니다 — 마이크로태스크가 우선순위를 가지기 때문입니다.
실용적 결과
js
// ❌ 단일 스레드를 막음 — UI가 5초간 멈춤
while (Date.now() - start < 5000) {}
긴 동기 작업은 모든 것(렌더링, 클릭)을 막습니다. 무거운 CPU 작업은 Web Worker(실제 별도 스레드)로 떠넘기세요.
왜 중요한가
이벤트 루프를 이해하면 비동기 순서, promise가 타이머보다 먼저 resolve되는 이유, 메인 스레드를 절대 막지 말아야 하는 이유를 설명할 수 있습니다 — 모든 비동기 JavaScript의 핵심 멘탈 모델입니다.
