Understanding the Event Loop
1. Understanding Event Loop Phases
libuv Phases (per tick)
┌─► timers (setTimeout, setInterval callbacks)
│ pending callbacks (deferred I/O)
│ idle, prepare (internal)
│ poll (retrieve new I/O events)
│ check (setImmediate callbacks)
│ close callbacks (e.g. socket.on("close"))
└─ (microtasks + nextTick run BETWEEN phases)
2. Understanding the Call Stack
| Concept | Description |
| Call stack | LIFO of currently executing functions |
| Stack overflow | Recursion too deep → RangeError |
| Single-threaded JS | One stack per isolate (worker = its own) |
3. Understanding the Callback Queue
| Queue | Source |
| Macrotask | Timers, I/O, setImmediate, close |
| Microtask | Promises, queueMicrotask |
| nextTick | process.nextTick (drained before microtasks) |
4. Using process.nextTick for Microtasks
Example
process.nextTick(() => console.log("a"));
Promise.resolve().then(() => console.log("b"));
console.log("c");
// c, a, b
Warning: Excessive nextTick chains can starve I/O.
| API | Phase |
setImmediate(fn) | Check phase (after I/O) |
setTimeout(fn, 0) | Timers phase (≥1ms) |
6. Understanding Microtasks vs Macrotasks
| Type | When Drained |
| nextTick queue | After current op, before microtasks |
| Microtask queue (Promises) | After nextTick, before next phase |
| Macrotask | One per phase iteration |
7. Understanding Promise Resolution Timing
Example: Order
setTimeout(() => console.log("timeout"), 0);
setImmediate(() => console.log("immediate"));
Promise.resolve().then(() => console.log("promise"));
process.nextTick(() => console.log("nextTick"));
// nextTick, promise, then timeout/immediate (order between those varies)
8. Avoiding Event Loop Blocking
| Source | Fix |
Sync FS (readFileSync) | Use async or streams |
| JSON.parse on huge strings | Stream parser |
| Heavy CPU (crypto, image) | Worker threads |
| Tight loops | Yield with setImmediate in batches |
9. Monitoring Event Loop Lag
Example: perf_hooks monitor
import { monitorEventLoopDelay } from "node:perf_hooks";
const h = monitorEventLoopDelay({ resolution: 20 });
h.enable();
setInterval(() => console.log("p99 lag(ms):", h.percentile(99)/1e6), 1000);
10. Using Event Loop Utilities
| API | Use |
perf_hooks.monitorEventLoopDelay | Histogram of lag |
perf_hooks.eventLoopUtilization() | Idle vs active |
queueMicrotask(fn) | Enqueue microtask |
setImmediate.__promisify__ | Promise-based wait |