Show me the phases of the event loop and where things run.
Node's loop is not one queue. It cycles through phases, and knowing which phase a callback belongs to explains most ordering surprises.
flowchart TD
T[timers<br/>setTimeout and setInterval callbacks] --> P[pending callbacks<br/>some deferred system errors]
P --> Po[poll<br/>waits for I/O, runs I/O callbacks]
Po --> C[check<br/>setImmediate callbacks]
C --> Cl[close<br/>socket and handle close events]
Cl --> TBetween every phase transition — and after each individual callback — the
microtask queues drain completely: process.nextTick first, then promise
callbacks. That is why an await resumes before any timer, no matter how short
the timer.
The poll phase is where a mostly-idle server spends its life, blocked in the kernel waiting for a socket to become readable. This is the crucial point about Node's model: waiting for I/O costs nothing, because the thread is parked in the operating system rather than spinning.
setImmediate runs in the check phase, so it fires after the current poll phase
completes; setTimeout(fn, 0) waits for the next timers phase. Despite the
names, setImmediate is generally the sooner of the two, which is exactly
backwards from what the names suggest.
process.nextTick is not a phase at all. It runs before promise callbacks and
before the loop advances, so recursive nextTick calls starve the loop
completely — no I/O, no timers, nothing. It exists for library internals that must
run before anything else, and application code almost always wants a promise.