Loading lessons...
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
- Timers: Executes callbacks scheduled by
setTimeout()andsetInterval(). - Pending Callbacks: Executes I/O callbacks deferred to the next loop iteration.
- Idle, Prepare: Used internally by Node.js.
- Poll: Retrieves new I/O events and executes I/O related callbacks.
- Check: Executes callbacks scheduled by
setImmediate(). - 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.nextTickand Promises) drain completely between every event loop step. - Synchronous code always runs to completion before async callbacks execute.