How the JavaScript Event Loop Actually Works
By Eduardo Dionisio
JavaScript is single-threaded, yet it handles timers, promises and network calls without freezing. The event loop is the machinery that makes this possible — and misunderstanding it is behind a huge share of interview mistakes.
The four parts
- Call stack — where synchronous code runs, one frame at a time.
- Task queue (macrotasks) — callbacks from setTimeout, setInterval, I/O.
- Microtask queue — promise .then/.catch, queueMicrotask, async/await continuations.
- Event loop — the coordinator that drains the microtask queue after every task.
The order that surprises people
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// prints: 1 4 3 2Synchronous code runs first (1, 4). Then, before the event loop even looks at the task queue, it drains the microtask queue (3). Only after microtasks are empty does the setTimeout callback (2) run — despite its 0ms delay.
Why this matters
A long-running synchronous loop blocks the whole thread — timers, promises and clicks all queue up behind it. A chain of promises that keeps adding microtasks can starve macrotasks indefinitely. And async/await is just sugar over promises, so it follows the same microtask rules.
If you can predict the output of a mixed setTimeout + promise + async example, you understand the event loop better than most. That is precisely what the JavaScript arena tests.