The event loop
JavaScript runs your code on one thread, yet a Node API serves hundreds of stores at once and an app stays responsive while it waits for the network. The event loop is how. This lesson shows the machinery step by step, how to predict the exact order of output, and what to do when one slow job freezes everyone.
By the end you can
- Draw the call stack, the platform's timers and I/O, the task queue and the microtask queue, and say what moves between them
- Predict the output order of code mixing
setTimeout, promises,awaitandqueueMicrotask - Explain why a long loop freezes a screen or an API, and measure event-loop lag in Node
- Name Node's extras (
process.nextTick,setImmediate, the loop phases) at a high level - Move a CPU-heavy job to a worker thread, or break it into chunks that let other work in
The everyday picture
Imagine a store with one cashier and one queue. The cashier serves one customer at a time and never does two things at once. Quick jobs, like scanning items and taking cash, she does right away. A slow job, like a home-delivery order that the back room has to pack, she does not wait for. She hands it to the back room, gives the customer a token number, and serves the next person. When the back room finishes, the token goes into a "ready" tray, and she calls it when she is free.
There is one more rule. Some tokens are priority tokens, such as "finish updating this customer's bill". Before the cashier calls the next customer from the queue, she always clears every priority token first, even ones that were added a moment ago. That is the whole event loop: one cashier (the thread), back-room helpers (the platform), a queue of tasks, and a priority queue of microtasks that is always emptied first.
One thread, a call stack, and helpers outside it
The JavaScript engine runs your code on a single thread. To know where it is, it keeps a call stack: calling a function pushes it on top, returning pops it off. Only the function on top runs. While the stack is not empty, nothing else in JavaScript can run, not a click handler, not a timer, not another request.
Slow things do not run on that thread. When you call setTimeout, fetch or fs.readFile, JavaScript hands the work to the platform: the browser's Web APIs, or in Node the libuv library and the operating system. The call returns at once, and your code continues. When the timer expires or the data arrives, the platform puts a callback in a queue. It never pushes it onto the stack directly; only the event loop does that, and only when the stack is empty.
There are two kinds of queue:
- The task queue (also called the macrotask queue): timer callbacks, I/O callbacks, network responses, clicks and taps.
- The microtask queue: promise reactions (
.then,.catch,.finally), the rest of anasyncfunction after anawait, and anything passed toqueueMicrotask.
The event loop repeats one cycle, forever: take one task and run it until the stack is empty; then run all microtasks, including any new ones added while doing so; then, in a browser, update the screen if it is time to; then take the next task. Running your script file from top to bottom is itself the first task.
Quick check
A for loop is busy for 2 seconds. Halfway through, the response to a fetch arrives. When does its callback run?
The platform receives the response on time and queues the callback, but only the event loop can move it onto the stack, and only when the stack is empty. Nothing is lost and nothing interrupts running JavaScript; the callback simply waits.