Chapter 2: Event Loop Mastery
Chapter 2 of 15
Chapter 2: Event Loop Mastery
2.1 Event Loop Phases
The Node.js event loop has six main phases that execute in order. Understanding these phases is crucial for writing performant Node.js code.
Event Loop Phases:
- Timers: Executes callbacks scheduled by setTimeout() and setInterval()
- Pending Callbacks: Executes I/O callbacks deferred to the next loop iteration
- Idle, Prepare: Internal use only
- Poll: Fetches new I/O events and executes I/O-related callbacks
- Check: Executes setImmediate() callbacks
- Close Callbacks: Executes close callbacks (e.g., socket.on('close'))
// Event loop execution order
console.log('1. Start');
setTimeout(() => console.log('2. Timer'), 0);
setImmediate(() => console.log('3. Immediate'));
process.nextTick(() => console.log('4. NextTick'));
Promise.resolve().then(() => console.log('5. Promise'));
console.log('6. End');
// Output:
// 1. Start
// 6. End
// 4. NextTick (highest priority)
// 5. Promise (microtask queue)
// 2. Timer (timers phase)
// 3. Immediate (check phase)
2.2 Process.nextTick and setImmediate
process.nextTick() and setImmediate() are used to schedule callbacks, but they execute at different times in the event loop.
// process.nextTick() - Executes before any other async operation
process.nextTick(() => {
console.log('nextTick 1');
});
process.nextTick(() => {
console.log('nextTick 2');
});
// setImmediate() - Executes in the check phase
setImmediate(() => {
console.log('immediate 1');
});
setImmediate(() => {
console.log('immediate 2');
});
// Output:
// nextTick 1
// nextTick 2
// immediate 1
// immediate 2
When to Use Each:
- process.nextTick(): When you need to execute code before the event loop continues
- setImmediate(): When you want to execute code after the current poll phase completes
2.3 Microtasks vs Macrotasks
// Microtasks (higher priority)
// - process.nextTick()
// - Promise.then()
// - queueMicrotask()
// Macrotasks (lower priority)
// - setTimeout()
// - setInterval()
// - setImmediate()
// - I/O callbacks
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
process.nextTick(() => console.log('4'));
queueMicrotask(() => console.log('5'));
console.log('6');
// Output: 1, 6, 4, 3, 5, 2
2.4 Blocking the Event Loop
// Bad: Blocks event loop
function blockingOperation() {
const start = Date.now();
while (Date.now() - start < 5000) {
// Blocking for 5 seconds
}
}
// Good: Non-blocking
function nonBlockingOperation() {
setTimeout(() => {
// Do work asynchronously
}, 0);
}
// Use worker threads for CPU-intensive tasks
const { Worker } = require('worker_threads');
const worker = new Worker('./cpu-intensive-task.js');
worker.postMessage({ data: largeData });