Search lands in PR-5.1 (Pagefind).

Explanation Intermediate

Chapter 10 Updated

libuv & The Event Loop

Six phases, two microtask queues, one single thread — the definitive event loop tour.

  • Full 35m
  • Revision 6m
  • Flow 2m

V8 + libuv split duties

One brain, one nervous system.

  • V8 (C++) runs JavaScript on the single main thread — call stack, heap, GC.
  • libuv (C) provides async I/O: event loop, thread pool (default 4), callback queues.
  • V8 delegates fs, https.get, crypto, DNS to libuv; libuv talks to the OS.
  • The main thread is sacred — nothing interrupts it; callbacks wait in queues.

Six phases, clockwise cycle

Timer → Pending → Idle → Poll → Check → Close.

  • Timer: setTimeout, setInterval — delay is a minimum, not exact.
  • Pending callbacks: deferred I/O callbacks (TCP errors etc).
  • Idle / Prepare: internal Node housekeeping.
  • Poll: I/O results from fs, http, net, crypto. Loop parks here if idle.
  • Check: setImmediate() — runs right after Poll.
  • Close: socket.on('close') cleanup.

Microtasks drain between every phase

Two VIP queues ahead of every macrotask.

  • Between every phase: drain ALL process.nextTick() → then ALL Promise callbacks.
  • A nextTick scheduled during draining still runs in the same drain.
  • Node 11+: microtasks also drain after every individual callback.
  • Recursive process.nextTick() can starve the event loop — use setImmediate() instead.

setTimeout(0) vs setImmediate

Order depends on context.

  • Outside I/O: order is non-deterministic — depends on process boot timing.
  • Inside I/O (Poll callback): setImmediate always beats setTimeout(0) — Check comes right after Poll.
  • setTimeout(fn, 0) still waits for Timers phase on the next loop iteration.
  • Poll → Check → Close → (back to Timers) is the fixed order.

Execution priority — 7 rungs

When everything is ready, who runs first.

  • (1) Synchronous code — always first.
  • (2) process.nextTick() — highest-priority microtask.
  • (3) Promise .then/.catch/.finally — second microtask tier.
  • (4) setTimeout / setInterval — Timers phase.
  • (5) I/O callbacks — Poll phase (fs, http, crypto).
  • (6) setImmediate() — Check phase.
  • (7) Close callbacks — final cleanup.

Seven golden rules to memorise

The cheat sheet for interviews.

  • Sync always runs first. Always. No exceptions.
  • process.nextTick() > Promises > every phase.
  • Microtasks drain between every phase.
  • Inside I/O, setImmediate beats setTimeout(0).
  • Outside I/O, that order is non-deterministic.
  • When idle, the loop parks in Poll, waiting for I/O.
  • Recursive nextTick() starves the loop — reach for setImmediate() for recursive async.

Comments

Comments are disabled in this environment. Set PUBLIC_GISCUS_REPO, PUBLIC_GISCUS_REPO_ID, and PUBLIC_GISCUS_CATEGORY_ID to enable.