libuv & the Event Loop Phases
The problem
JavaScript's single-threaded, non-blocking model works in a browser because the browser provides the underlying async machinery (timers, network stacks) — outside a browser, something still has to provide that machinery, including things browsers don't need, like file system access and OS-level asynchronous I/O.
Why now
The event loop already established the call-stack-then-queue model in the abstract; Node doesn't reimplement that model from scratch, it delegates to libuv, a C library with its own concrete, ordered phases — this topic is where the browser-agnostic theory meets one specific, real, more complicated implementation.
Mental model
Node's event loop isn't one queue, it's several, run in a fixed order every tick: timers (setTimeout/setInterval callbacks), pending callbacks, poll (I/O events, the phase where Node spends most of its time waiting), check (setImmediate), and close callbacks — plus two special microtask queues (process.nextTick and Promises) that drain completely between every single phase, not just once per tick. Knowing the phase order is what makes 'why did setImmediate run before setTimeout(fn, 0) this time' answerable instead of mysterious.
Requires
- The Event LoopJavaScript Runtime
libuv's phases are Node's concrete, more detailed implementation of exactly the call-stack-then-queue model the-event-loop already established; you need that general model before its specific, multi-queue Node implementation is legible rather than overwhelming.
Unlocks
Used in
- Every Node.js server's request handling, timers, and file I/O
- Explaining surprising callback ordering between setTimeout, setImmediate, and process.nextTick
- Diagnosing 'why is my server unresponsive' issues rooted in blocking the poll phase
- Understanding why process.nextTick callbacks can, if misused, starve the event loop entirely
Projects
- Write a script mixing setTimeout(fn, 0), setImmediate(fn), and process.nextTick(fn) at the top level and inside an I/O callback, predict the output order, then run it and reconcile any surprises against the phase model
- Write a recursive process.nextTick call with no exit condition and observe it starving the event loop entirely, never reaching the timers or poll phases
Examples
- setImmediate always runs before a setTimeout(fn, 0) scheduled inside an I/O callback, because the check phase comes before the next timers phase in that specific context
- process.nextTick callbacks run to completion before the event loop proceeds to the next phase at all — powerful, but capable of starving I/O if used recursively without care
Resources
Mastery checklist
- I can list libuv's event loop phases in order and name what runs in each
- I can predict the execution order of mixed setTimeout/setImmediate/process.nextTick code
- I can explain why process.nextTick can starve the event loop if misused
- I can diagnose an unresponsive Node server as a blocked poll phase