1. Courses
  2. Developer Foundations
  3. JavaScript Deep Dive
  4. Stop 6

40 minAdvanced

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, await and queueMicrotask
  • 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 : 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 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 (also called the macrotask queue): timer callbacks, I/O callbacks, network responses, clicks and taps.
  • The : promise reactions (.then, .catch, .finally), the rest of an async function after an await, and anything passed to queueMicrotask.

The 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?

Call stack one thread checkout() main script top one runs Platform Web APIs or libuv timer: 0 ms fetch /stock readFile off the thread hand off when done Task queue one per turn .then, await, queueMicrotask Microtask queue all of them Event loop waits for an empty stack 1. drain all 2. take one 3. run
The machinery. Code on the call stack hands slow work to the platform (Web APIs in a browser, libuv in Node). Finished callbacks wait in the task queue; promise callbacks wait in the microtask queue. Whenever the stack is empty, the loop runs every microtask first (1), then takes one task (2), and pushes each callback onto the stack to run (3).

Loading the rest of the lesson…