The Event Loop Phases

The event loop cycles through ordered phases to run your callbacks.

The event loop is what lets single-threaded Node handle many tasks. On each cycle it moves through ordered phases, running the callbacks queued in each.

The main phases of the Node.js event loopTimerssetTimeout, setIntervalPending callbacksdeferred I/O callbacksPollretrieve I/O eventsChecksetImmediate callbacksClose callbackssocket close eventsloop
Each cycle runs timers, then I/O, then check and close callbacks — and repeats.

Example

Example · javascript
console.log('1: sync');

setTimeout(() => console.log('4: timer'), 0);
setImmediate(() => console.log('5: immediate'));
Promise.resolve().then(() => console.log('3: promise'));
process.nextTick(() => console.log('2: nextTick'));

// Typical order: 1, 2, 3, 4, 5

When to use it

  • A performance engineer traces why a timer callback fires 50 ms late by understanding that the timers phase only runs after the poll phase drains.
  • A server developer ensures that a post-response cleanup function runs in the check phase via `setImmediate` rather than blocking the next I/O cycle.
  • A library author uses knowledge of event-loop phases to decide whether to schedule a recursive operation with `process.nextTick` or `setImmediate`.

More examples

Six phases in order

Documents the six event-loop phases and shows that `setImmediate` fires in the check phase, after poll.

Example · js
// Phases (simplified):
// 1. timers       -- setTimeout / setInterval callbacks
// 2. pending I/O  -- deferred I/O errors
// 3. idle/prepare -- internal
// 4. poll         -- incoming I/O events (most callbacks here)
// 5. check        -- setImmediate callbacks
// 6. close        -- close event callbacks

setTimeout(() => console.log('timers phase'), 0);
setImmediate(() => console.log('check phase'));
// Output order depends on context, but inside I/O:
// check phase -> timers phase

Execution order demonstration

Inside an I/O callback, `setImmediate` consistently fires before `setTimeout(fn,0)` because the check phase comes before the timers phase re-runs.

Example · js
const fs = require('fs');

fs.readFile(__filename, () => {
  // Inside an I/O callback: setImmediate always before setTimeout
  setTimeout(  () => console.log('setTimeout'),   0);
  setImmediate(() => console.log('setImmediate'));
});
// Output: setImmediate -> setTimeout

nextTick runs before each phase

Shows that `process.nextTick` and Promise microtasks drain before each event-loop phase transition.

Example · js
process.nextTick(() => console.log('1: nextTick'));
Promise.resolve().then(() => console.log('2: microtask'));
setImmediate(     () => console.log('3: setImmediate'));

console.log('0: sync');
// Output: 0 -> 1 -> 2 -> 3

Discussion

  • Be the first to comment on this lesson.