🔄

The Event Loop — Why setTimeout(fn, 0) Doesn't Execute Immediately

Execution order of Call Stack, Task Queue, and Microtask Queue

console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// Output: 1, 4, 3, 2

Why does 0ms timeout come after Promise? Event loop execution order.

3 Queues

Call Stack — currently executing function. Sync code runs here sequentially.

Microtask Queue — Promise.then, MutationObserver, queueMicrotask. When Call Stack empties, all Microtasks drain completely before anything else. Priority over Task Queue.

Task Queue (Macro Task) — setTimeout, setInterval, I/O callbacks, requestAnimationFrame. One at a time, only after Microtasks are fully drained.

V8 Implementation

V8 itself doesn't implement the event loop. Browsers use libevent/OS event loop; Node.js uses libuv. V8 only manages Call Stack and Microtask Queue.

setTimeout(fn, 0) minimum delay is actually 4ms in browsers (HTML spec).

Key Points

1

All sync code on Call Stack must finish before async callbacks execute

2

Microtasks (Promise.then) always execute before Tasks (setTimeout)

3

Microtask Queue drains completely — no Tasks interleaved

4

V8 manages only Call Stack + Microtasks; event loop is browser/libuv responsibility

Use Cases

Debugging async execution order — why this callback runs before that one UI responsiveness — break heavy work with setTimeout for rendering opportunities