The Event Loop

One Thread, Many Pending Operations

The Event Loop

Node runs JavaScript on a single thread. The event loop coordinates pending async operations — IO, timers, promises, immediates.

4 min read Level 2/5 #nodejs#event-loop#async
What you'll learn
  • Understand single-threaded vs concurrent
  • Recognize event loop phases
  • Know what blocks the loop

Node runs your JavaScript on one thread. The reason it handles thousands of concurrent connections without breaking a sweat is the event loop.

The Mental Model

Imagine a kitchen with one cook:

  1. You order a pizza (slow). The cook puts it in the oven and moves on.
  2. You order a salad (fast). The cook makes it.
  3. The pizza pings — cook grabs it from the oven.
  4. You order more salads. Each goes out fast.

The cook never waits on the oven — they keep handling fast orders while slow ones cook in the background.

Node is the cook. Your CPU-light JS code is the fast orders. IO (files, network, DB) is the pizza in the oven.

Phases

Each loop iteration (“tick”) visits these phases in order:

PhaseWhat runs
TimerssetTimeout, setInterval callbacks whose time is up
Pending callbacksA few I/O callbacks from previous loop
Idle, prepareInternal
PollMost I/O callbacks (file reads, sockets)
ChecksetImmediate callbacks
Closeclose event callbacks

Between every phase: queued microtasks (promise .then, queueMicrotask) run.

Microtasks vs Macrotasks

console.log("1");

setTimeout(() => console.log("2 — timeout"), 0);
Promise.resolve().then(() => console.log("3 — promise"));

console.log("4");

// Output:
// 1
// 4
// 3 — promise
// 2 — timeout

Promises (microtasks) flush before the next macrotask phase, so 3 prints before 2.

What Blocks the Loop

Synchronous CPU-heavy work blocks every other request:

// imagine 10 concurrent requests hit this handler
function handler(req, res) {
  // pure CPU work — blocks for ~500ms
  const result = expensiveSorting();
  res.end(result);
}

The 11th request waits ~5 seconds. The fix: move CPU work to a worker thread (covered later this chapter).

Don’t Block

The two ways to block:

  1. Long synchronous code — heavy loops, sync crypto, sync fs
  2. Sync IO inside async pathsreadFileSync in a handler

Async by default. CPU work → workers. IO → promises.

Callbacks →