Lesson 3 +10 XP

The Non-Blocking Event Loop

The Event Loop Architecture

Node.js executes JavaScript on a single thread, but delegates heavy I/O operations (file system, network calls) to the operating system kernel or libuv worker thread pool.

The 6 Phases of the Node.js Event Loop

  1. Timers: Executes callbacks scheduled by setTimeout() and setInterval().
  2. Pending Callbacks: Executes I/O callbacks deferred to the next loop iteration.
  3. Idle, Prepare: Used internally by Node.js.
  4. Poll: Retrieves new I/O events and executes I/O related callbacks.
  5. Check: Executes callbacks scheduled by setImmediate().
  6. Close Callbacks: Executes socket/handle close callbacks (e.g., socket.on('close')).

Microtasks vs Macrotasks

Microtasks execute immediately after the current operation, before moving to the next Event Loop phase:

  • process.nextTick() (Highest priority microtask)
  • Promise callbacks (.then(), .catch(), async/await)
console.log("1. Sync Start");

setTimeout(() => console.log("2. Timer (Macrotask)"), 0);

Promise.resolve().then(() => console.log("3. Promise (Microtask)"));

process.nextTick(() => console.log("4. nextTick (Priority Microtask)"));

console.log("5. Sync End");

// Output Order:
// 1. Sync Start
// 5. Sync End
// 4. nextTick (Priority Microtask)
// 3. Promise (Microtask)
// 2. Timer (Macrotask)

TL;DR

  • Node.js uses libuv to manage the C++ thread pool and event queue.
  • Microtasks (process.nextTick and Promises) drain completely between every event loop step.
  • Synchronous code always runs to completion before async callbacks execute.