The JavaScript event loop explained simply
event loop, call stack, web APIs, callback queue, microtask queue, task queue, setTimeout behavior, synchronous vs asynchronous, non-blocking I/O
How JavaScript Handles Async Without Threads
JavaScript is single-threaded but handles async operations efficiently through the event loop. Understanding it is essential for predicting execution order โ one of the most-asked JavaScript interview topics and a source of genuine runtime bugs.
The Sequence
Synchronous code runs on the call stack to completion.
Async operations (timers, fetch, I/O) are handed off to browser or Node.js Web APIs.
When those complete, their callbacks are pushed into a queue.
The event loop checks: is the stack empty? If yes, it moves the next queued callback onto the stack.
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Output order: 1, 4, 3, 2
Microtask Queue vs Callback Queue
Promise callbacks go into the microtask queue, which is fully drained between each macrotask (setTimeout, setInterval). This is why Promise .then callbacks always run before setTimeout callbacks โ even with a 0ms delay. The microtask queue must be empty before the event loop picks the next macrotask.
Why This Matters in Practice
Long synchronous operations block the entire call stack โ no UI updates, no event handling, nothing. Heavy computation must be chunked or offloaded to Web Workers to keep applications responsive.
