Node.js Event Loop - Deep Dive into Libuv Phases & Microtask Queue Priorities

The Event Loop is the single most important architectural concept in Node.js. It explains how Node can serve hundreds of thousands of concurrent network connections without allocating thousands of operating system threads. To pass senior engineering interviews and master high-throughput systems, you must understand the six distinct execution phases of Libuv.

Node.js Libuv Event Loop Phases and Microtask Queues
Figure 5: The 6 Execution Phases of the Libuv Event Loop & Microtask Interrupt Priority

The 6 Execution Phases of Each Event Loop Tick

  1. 1. Timers Phase: Executes callbacks scheduled by setTimeout() and setInterval() whose threshold time has elapsed. (Note: timers specify the minimum delay before execution, not an exact guaranteed moment).
  2. 2. Pending Callbacks: Executes I/O callbacks deferred from the previous loop iteration (e.g., specific operating system errors like TCP socket ECONNREFUSED).
  3. 3. Idle, Prepare: Used strictly internally by Libuv for subsystem maintenance before each poll cycle.
  4. 4. Poll Phase: The core of the loop. Retrieves new I/O events (incoming network requests, file reads, database responses) and executes their callbacks. If no timers are ready, it blocks and waits for new incoming traffic.
  5. 5. Check Phase: Dedicated exclusively to running callbacks scheduled via setImmediate(). This phase executes immediately after the Poll phase completes.
  6. 6. Close Callbacks: Executes cleanup handlers when sockets or handles close abruptly (e.g. socket.on('close', ...)).

The Microtask Queue: The Phase Interrupter

There are two internal queues that are not part of Libuv's phases, but take priority above everything else:

  • process.nextTick() Queue (Highest Priority)
  • Promise Microtask Queue (Promise.then(), catch(), finally(), await)
Crucial Rule: Microtask Priority

Whenever the current Call Stack becomes empty—regardless of which Libuv phase is currently active—Node.js drains the entire process.nextTick queue followed by the Promise microtask queue before moving to the next loop phase or callback.

Famous Interview Output Prediction Challenge:

Can you predict the exact output order of this script?

// event-loop-quiz.js
console.log('1. Synchronous Main Script');

setTimeout(() => {
  console.log('2. setTimeout (Timers Phase)');
}, 0);

setImmediate(() => {
  console.log('3. setImmediate (Check Phase)');
});

Promise.resolve().then(() => {
  console.log('4. Promise.then (Microtask Queue)');
});

process.nextTick(() => {
  console.log('5. process.nextTick (Highest Priority Microtask)');
});

console.log('6. Synchronous Script End');

Output & Execution Breakdown:

1. Synchronous Main Script
6. Synchronous Script End
5. process.nextTick (Highest Priority Microtask)
4. Promise.then (Microtask Queue)
2. setTimeout (Timers Phase)
3. setImmediate (Check Phase)

Frequently Asked Questions (FAQ) - Student & Interview Review

Q1. What is the Node.js Event Loop?

The Event Loop is the central coordination mechanism provided by the Libuv C library that enables Node.js to perform non-blocking asynchronous I/O operations despite JavaScript execution being single-threaded. It constantly monitors the call stack, polls the OS for completed events, and distributes callback tasks across 6 distinct phases.

Q2. What are the 6 phases of the Libuv Event Loop in exact order?

The 6 phases executed in each tick of the event loop are: 1) Timers (setTimeout, setInterval), 2) Pending I/O Callbacks, 3) Idle / Prepare, 4) Poll Phase (retrieves new I/O events, executes I/O callbacks, waits for connections), 5) Check Phase (setImmediate callbacks), and 6) Close Callbacks (e.g. socket.on('close')).

Q3. What is the Poll phase and why is it considered the heart of Libuv?

The Poll phase calculates how long it should block and wait for new I/O events, and then processes events from the poll queue. If the queue is not empty, it iterates through and executes callbacks synchronously. If the queue is empty and there are no setImmediate or timer scripts waiting, it blocks and waits for new incoming connections or file reads.

Explore Related Node.js Tutorials & Next Steps

Boost your server-side JavaScript proficiency by studying these closely connected core modules:

Timers, setImmediate & nextTick

Detailed comparison of timer execution priorities.

Node.js Architecture & V8

How V8 and Libuv collaborate during loop execution.

Worker Threads & CPU Offloading

Prevent long-running tasks from starving the event loop.

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Stumbleupon Share on Tumblr Share on Reddit Share on Diggit

You may also like this!