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 is single-threaded, meaning it can only execute one piece of code at a time on the call stack, yet it clearly handles timers, network requests, and user interactions without freezing the page. The event loop is the mechanism that makes this possible, and it is one of the most tested conceptual topics in JS interviews because it requires connecting several moving parts into one coherent mental model.

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 tracks function invocations: every time a function is called, a frame is pushed; when it returns, the frame is popped. Synchronous code runs entirely on this stack. Asynchronous operations — setTimeout, DOM events, network requests — are handed off to Web APIs (in browsers) or libuv (in Node), which run outside the JS thread and, once complete, place a callback into a queue rather than executing it immediately.
setTimeout
2
There are actually two queues that matter: the macrotask (or 'task') queue, which holds things like setTimeout callbacks and DOM event callbacks, and the microtask queue, which holds Promise callbacks (.then, .catch, .finally) and queueMicrotask calls. The event loop's job is simple but strict: after the call stack empties, it drains the *entire* microtask queue before touching a single macrotask, and this repeats before every new macrotask is processed. This is why a resolved Promise's .then() always runs before a setTimeout(fn, 0), even though both were scheduled at roughly the same time.
setTimeout.then.catch.finallyqueueMicrotask
3
Understanding this ordering is essential for debugging race conditions, explaining unexpected output ordering in interviews, and reasoning about performance — long-running synchronous code blocks the entire loop, freezing the UI, since nothing else can run until the stack clears.