Mastering the Event Loop: The Heart of Node.js
how can it handle thousands of concurrent connections with just one thread? The answer lies in the Event Loop.
What is the Event Loop?
It is the mechanism that allows Node.js to perform non-blocking I/O operations. Think of a waiter in a restaurant: they don’t stand by the table waiting for the chef to cook the meal. They take the order, bring it to the kitchen, and move to the next table. Once the food is ready, they return to serve it.
The Main Phases
The loop isn't chaotic; it follows a strict sequence:
Timers: Executes
setTimeoutandsetIntervalcallbacks that have expired.Poll: This is where Node looks for new I/O events (file reading, network). This is where the loop spends most of its time.
Check: Specifically reserved for
setImmediatecallbacks.
Microtasks vs Macrotasks: The Invisible Hierarchy
This is a common pitfall in technical interviews.
Macrotasks: These are standard loop events (timers, I/O).
Microtasks: These include Promises and
process.nextTick().
The Golden Rule: Microtasks are executed immediately after the current phase of the Event Loop finishes, before moving on to the next macrotask. If you flood your code with recursive microtasks, you will "starve" the Event Loop, and it will never reach the I/O phase.
In what order does my code execute?
Title: Microtasks, Macrotasks, and Execution Order in Node.js
Have you ever wondered why a Promise executes before a setTimeout(0)? The answer lies in queue priority. Let’s look at it with a real-world example you might encounter in a technical interview.
The Code Experiment
Take a look at this block and try to guess the console output order:
JavaScript
console.log('1. Start (Synchronous)');
setTimeout(() => console.log('2. Timeout (Macrotask)'), 0);
setImmediate(() => console.log('3. Immediate (Check phase)'));
Promise.resolve().then(() => console.log('4. Promise (Microtask)'));
process.nextTick(() => console.log('5. NextTick (Top Priority)'));
console.log('6. End (Synchronous)');
The Result Explained
Synchronous First:
1and6are printed. Synchronous code blocks the loop until it's done.Draining Microtasks: Before the loop moves to the Timers phase, it clears the microtask queues:
process.nextTickwins: Prints5.Promises are next: Prints
4.
Event Loop Kicks In:
Timers Phase: It sees the 0ms
setTimeouthas expired. Prints2.Check Phase:
setImmediateis executed. Prints3.
Final Order: 1, 6, 5, 4, 2, 3.
Why does this matter? If you overuse
process.nextTick, you can cause "I/O Starvation," preventing the loop from ever reaching the file system or network phases.