The Event Loop
Full Event Loop Diagram
┌───────────────────────────┐
┌─>│ timers │ (setTimeout, setInterval)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │ (I/O callbacks from previous loop)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │ (internal Node.js use)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐ ┌───────────────┐
│ │ poll │<─────┤ incoming: │
│ │ (retrieve new I/O events)│ │ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │ (setImmediate callbacks)
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │ (e.g., socket.on('close'))
└───────────────────────────┘
Phase Descriptions
| Phase | What Happens |
|---|---|
| Timers | Executes setTimeout() and setInterval() callbacks |
| Pending Callbacks | Executes I/O callbacks deferred from previous loop |
| Idle, Prepare | Internal Node.js operations |
| Poll | Retrieves new I/O events, executes I/O callbacks (most time spent here) |
| Check | Executes setImmediate() callbacks |
| Close Callbacks | Executes close event callbacks (e.g., socket.on('close')) |
Microtasks (Run Between Every Phase!)
Critical: Between EVERY phase, Node.js runs microtasks:
┌─ Microtasks (run between EVERY phase) ─┐
│ 1. process.nextTick() queue │ ← Highest priority
│ 2. Promise callbacks (.then, .catch) │ ← Second priority
└─────────────────────────────────────────┘
Complete Execution Order
Execution Stack (Synchronous code)
↓
process.nextTick queue ← Runs first
↓
Promise microtask queue ← Runs second
↓
┌─────────────────────┐
│ Timers Phase │
├─────────────────────┤
│ [microtasks run] │ ← After each phase!
├─────────────────────┤
│ Pending Callbacks │
├─────────────────────┤
│ [microtasks run] │
├─────────────────────┤
│ Poll Phase │
├─────────────────────┤
│ [microtasks run] │
├─────────────────────┤
│ Check Phase │ ← setImmediate
├─────────────────────┤
│ [microtasks run] │
├─────────────────────┤
│ Close Callbacks │
├─────────────────────┤
│ [microtasks run] │
└─────────────────────┘
↓
Repeat forever
Example: Complete Order
console.log('1: Sync');
setTimeout(() => console.log('2: setTimeout'), 0);
setImmediate(() => console.log('3: setImmediate'));
process.nextTick(() => console.log('4: nextTick'));
Promise.resolve().then(() => console.log('5: Promise'));
console.log('6: Sync');
Output:
1: Sync
6: Sync
4: nextTick ← Microtask (highest priority)
5: Promise ← Microtask
2: setTimeout ← Timer phase
3: setImmediate ← Check phase
Explanation:
- All synchronous code runs first (1, 6)
- Microtasks run before entering event loop phases:
process.nextTick()first (4)- Promises second (5)
- Event loop phases begin:
- Timers phase (2)
- Poll phase (nothing here)
- Check phase (3)