JavaScript runs one thing at a time, yet it handles clicks, timers and network replies. The event loop is how.
The pieces
- Call stack: where the code that is running right now lives. One thing at a time.
- Web APIs: the browser’s own machinery (timers, network, events) that works outside your code.
- Task queue: callbacks that are ready to run (timers, events), waiting for the stack to be empty.
- Microtask queue: a higher-priority queue for promise handlers and
queueMicrotask.
The event loop does this forever:
- Run the current code until the call stack is empty.
- Run all microtasks.
- Take the next task from the task queue and run it.
- (Let the browser repaint if needed.) Repeat.
A puzzle
What order does this print?
console.log("1. script start");
setTimeout(() => console.log("5. timeout"), 0);
Promise.resolve().then(() => console.log("3. promise"));
queueMicrotask(() => console.log("4. microtask"));
console.log("2. script end");
The answer: 1, 2, 3, 4, 5. Here is why:
script startandscript endare normal code, so they run first, in order.- The
setTimeoutcallback goes to the task queue. - The promise handler and the microtask go to the microtask queue.
- When the script finishes, the loop runs all microtasks (3 and 4) before the next task (5).
So even with a delay of 0, a timer runs after every pending promise handler.
What this means in practice
- Promise code (
then,awaitcontinuation) runs sooner than timers. - A long synchronous loop or heavy calculation blocks everything, including clicks and screen updates, because the loop cannot take the next task.
- An endless chain of microtasks can starve the page, because it never gets to the next task.
await in the picture
Code after an await continues as a microtask once the promise settles.
async function demo() {
console.log("A");
await null;
console.log("C");
}
demo();
console.log("B");
// the order is A, B, C
Why the page freezes
button.addEventListener("click", () => {
while (true) {} // nothing else can ever run: the stack never empties
});
If your code runs a long job, the page cannot respond. Split the work into small pieces, use a Web Worker (Web Workers), or find a faster way.
You do not need to memorise it
Remember three things: synchronous code first, then promises (microtasks), then timers and events (tasks), and never block the stack.