Phases of the Event Loop
Node.js's event loop runs through a fixed set of phases repeatedly, each dedicated to a specific kind of callback. Unlike the browser's simpler task/microtask model, Node.js has several distinct phases for different sources of async work.
Main Phases (Simplified)
| Phase | Handles |
|---|---|
| Timers | Callbacks scheduled by setTimeout and setInterval |
| Pending Callbacks | Some system-level callbacks deferred from the previous cycle |
| Poll | Fetching new I/O events; executes I/O callbacks (like a completed file read) |
| Check | setImmediate() callbacks |
| Close Callbacks | Cleanup, like socket "close" events |
setTimeout vs setImmediate
Console Output
Click “Run” to see the console output here.
Microtasks Run Between Every Phase
Promise callbacks (via .then()) and process.nextTick() are microtasks — they run to completion after the currently executing operation finishes, before the event loop proceeds to the next phase. process.nextTick() has even higher priority than resolved promises.
Ordering Example
Console Output
Click “Run” to see the console output here.
Why This Matters in Practice
You rarely need to reason about exact phase ordering day to day — but understanding that promises resolve before timers, and that nextTick has the highest priority, explains a lot of subtle async bugs when they do come up.
Best Practice
Avoid process.nextTick() recursion (calling nextTick from within a nextTick callback repeatedly) — because it runs before the event loop can proceed to I/O, an unbounded recursive chain can starve I/O entirely.