Back to 20 Concepts
event-loop • Advanced
The 6-Phase Libuv Event Loop (Timers, Poll, Check)
Each tick of the Libuv Event Loop processes 6 distinct phases: Timers -> Pending Callbacks -> Idle/Prepare -> Poll -> Check -> Close Callbacks.
Intuitive Mental Model
The Clock Face with 6 Stations: The train visits Station 1 (Timers: setTimeout), Station 2 (I/O Errors), Station 3 (Internal), Station 4 (Poll: incoming network/disk events), Station 5 (Check: setImmediate), and Station 6 (Close: socket.on("close")).
Node.js ESM / CJS ImplementationNode.js v22 LTS
import fs from 'fs';
// Timers Phase:
setTimeout(() => console.log('1. setTimeout (Timers Phase)'), 0);
// Check Phase:
setImmediate(() => console.log('2. setImmediate (Check Phase)'));
// Poll Phase (I/O):
fs.readFile('./package.json', () => {
console.log('3. I/O Callback (Poll Phase)');
// Inside I/O cycle, setImmediate ALWAYS executes before setTimeout!
setTimeout(() => console.log('5. setTimeout in I/O'), 0);
setImmediate(() => console.log('4. setImmediate in I/O'));
});Key Architectural Takeaways
- •1. Timers: Executes callbacks scheduled by setTimeout() and setInterval().
- •2. Poll: Retrieves new I/O events (network packets, file reads) and executes their callbacks. Blocks if no other phases are queued.
- •3. Check: Executes setImmediate() callbacks immediately after the Poll phase.
- •Inside an I/O callback, setImmediate is guaranteed to run before setTimeout(..., 0).
Common Production Mistake
Assuming setTimeout(fn, 0) runs in 0ms; operating systems enforce a minimum 1ms timer threshold.
Recommended Solution
Use setImmediate() when you want a callback to execute immediately after the current Poll cycle.