Fundamentalsintermediate

The Event Loop and Call Stack

How JavaScript's single-threaded call stack cooperates with the event loop, task queue, and microtask queue to handle asynchronous work.

JavaScript runs one thing at a time and still handles timers, requests and clicks without freezing. The event loop is how.

Think of the call stack as a single cashier who can only serve one customer at a time. The microtask queue is VIP customers who get served completely before the cashier looks at the regular line (macrotask queue) again — even if a VIP walks in while another VIP is already being helped.

Key Concepts

1
The call stack runs synchronous code. A call pushes a frame; a return pops it. Nothing else can run while the stack has frames on it.
2
Asynchronous work is handed off. setTimeout, a network request, a DOM event — these go to the browser (or libuv in Node), which is not the JavaScript thread. When they finish, a callback is placed in a queue.
setTimeout
3
There are two queues, and the order between them is the whole topic.
    macrotask queue   setTimeout, setInterval, DOM events, I/O
    microtask queue   Promise callbacks, queueMicrotask,
                      MutationObserver
4
The loop's rule: when the stack empties, drain the entire microtask queue, then take one macrotask, then drain microtasks again.
5
Which produces the output people are asked to predict.
    console.log('1');
    setTimeout(() => console.log('2'));
    Promise.resolve().then(() => console.log('3'));
    console.log('4');
6
    // 1 4 3 2
7
1 and 4 are synchronous. Then the stack empties. 3 is a microtask and runs before 2, which is a macrotask — even though the timeout was scheduled first.
1432
8
Microtasks can starve the loop.
    function loop() { Promise.resolve().then(loop); }
    loop();   // the page freezes
9
Each microtask queues another, and the queue is drained completely before anything else runs. So the rendering and the timers never get a turn. A recursive setTimeout does not do this, because each one is a separate macrotask.
setTimeout
10
setTimeout(fn, 0) is not zero. It means "queue this as a macrotask", and the browser clamps nested timeouts to about 4 ms. It runs after the current synchronous code and after all pending microtasks.
setTimeout(fn, 0)
11
Why blocking matters. A long synchronous loop holds the stack, so no callback runs, no event is handled, and the page cannot even repaint.
12
    for (let i = 0; i < 1e10; i++) {}   // the tab is frozen
13
Breaking such work into chunks with setTimeout, or moving it to a Web Worker, is the fix — because the constraint is the single thread, not the algorithm.
setTimeout
14
What the interviewer is probing.1. "Walk through what happens when you call setTimeout with 0ms." Probing: the queue model. Stalls: "It runs immediately." Moves up: the callback goes to the task queue and runs only after the stack empties and all pending microtasks drain.
15
2. "Which runs first, a promise callback or a setTimeout?" Probing: microtasks versus macrotasks. Stalls: "Whichever was scheduled first." Moves up: the promise — the microtask queue drains completely between every macrotask.
16
3. "What blocks the event loop?" Probing: the single thread. Stalls: "Nothing, it is async." Moves up: any synchronous work — a long loop or a huge JSON parse — because the loop cannot proceed until the stack clears.
17
4. "How do you run heavy computation without freezing the UI?" Probing: the escape hatches. Stalls: "Use async/await." Moves up: a Web Worker on another thread, or chunk the work and yield between pieces; async does not create parallelism.