Event Loop Phases
Overview
The Event Loop is the beating heart of Node.js. It is a C++ while loop inside libuv that constantly checks if there is any asynchronous work to finish. But it doesn't just pull tasks randomly. The Event Loop is strictly divided into 'Phases', and it processes callbacks in a very specific mathematical order: 1. Timers (setTimeout), 2. I/O Callbacks, 3. Idle/Prepare (internal), 4. Poll (incoming connections/data), 5. Check (setImmediate), 6. Close (socket.on('close')).
Syntax
// The Event Loop processes tasks in this order!
// 1. Timers Phase
setTimeout(() => console.log('1. Timer Phase'), 0);
// 4. Poll Phase (File I/O)
const fs = require('fs');
fs.readFile(__filename, () => {
console.log('2. Poll Phase (I/O finished)');
// 5. Check Phase (Runs immediately after Poll)
setImmediate(() => console.log('3. Check Phase'));
});Common Pitfalls
- Assuming
setTimeout(fn, 0)will execute instantly. It does NOT. It guarantees it will execute no earlier than 0 milliseconds. If the Event Loop is currently bogged down in the Poll phase reading a massive file, the timer will have to wait until the loop cycles back to the Timers phase. - Starving the Event Loop with Microtasks. Promises (
.then) andprocess.nextTick()do NOT belong to any of the 6 phases. They are 'Microtasks'. Node checks the Microtask queue after every single operation. If you recursively chain Promises, the Event Loop will freeze permanently trying to clear the Microtask queue, preventing Timers or I/O from ever running.
Interview Questions
setImmediate execute BEFORE setTimeout(fn, 0) if both are placed inside a File Read callback?Because a File Read callback executes in the Poll phase. The very next phase in the Event Loop architecture is the Check phase (where setImmediate lives). The Loop will execute the Check phase instantly, and won't hit the Timers phase until it loops all the way back around to the top.
Real-World Example
Visualizing the hidden Microtask Queue intercepting the Event Loop.
setTimeout(() => console.log('Event Loop: Timer Phase'), 0);
Promise.resolve().then(() => {
// This is a Microtask! It gets VIP priority.
// It will execute BEFORE the Timer Phase, even though the timer was 0ms.
console.log('Microtask: Promise resolved!');
});
console.log('Main Thread (Runs First!)');Check Your Knowledge
Test your understanding of Event Loop Phases with these quick questions.