100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Node.js

Node.js Architecture and the Event Loop

Understand Node.js's single-threaded, non-blocking architecture and how the event loop phases process callbacks.

Introduction to Node.jsIntermediate12 min readJul 8, 2026
Analogies

Introduction

Node.js runs JavaScript on a single main thread, yet it can handle thousands of concurrent connections without blocking. This is possible because of its event-driven architecture: expensive I/O operations (reading files, network requests, database queries) are offloaded to the operating system or a background thread pool, while the main thread stays free to keep executing code. The event loop is the mechanism that coordinates this — it continuously checks whether there is work to do and dispatches callbacks when asynchronous operations complete.

🏏

Cricket analogy: A single umpire can officiate an entire match without stopping play by delegating ball-tracking to Hawk-Eye and DRS reviews to the third umpire, freeing them to keep the game moving, much like Node offloading I/O to the OS.

How It Works

At a high level, Node.js architecture has three layers: your JavaScript code, the Node.js APIs/bindings (fs, http, etc.), and libuv, which manages the event loop and a thread pool for operations that cannot be done asynchronously at the OS level (like some file system calls). The call stack executes synchronous code first. Asynchronous operations are registered with libuv or the OS, and when they complete, their callbacks are placed into queues. The event loop repeatedly checks the call stack: if it's empty, it pulls callbacks from these queues and pushes them onto the stack to execute.

🏏

Cricket analogy: The captain (JavaScript code) gives instructions, the team management staff (Node APIs) translate them into ground operations, and the groundskeeper (libuv) manages the actual pitch prep and player rotations behind the scenes.

The event loop has distinct phases, executed in order on each iteration ('tick'): timers (executes callbacks scheduled by setTimeout/setInterval), pending callbacks (I/O callbacks deferred from the previous cycle), poll (retrieves new I/O events and executes their callbacks; this is where most I/O callbacks run), check (executes setImmediate callbacks), and close callbacks (e.g. socket.on('close', ...)). Between each phase, and after each callback on the call stack finishes, Node.js drains the microtask queue, which holds Promise callbacks (.then/.catch/.finally) and process.nextTick callbacks. process.nextTick callbacks run before other microtasks (Promises), and both run before the event loop proceeds to the next phase.

🏏

Cricket analogy: An over goes through fixed phases: bowler's run-up (timers), umpire signals from last over (pending callbacks), the actual delivery and shot (poll), instant replay checks (check), and the fielder returning the ball (close) — with DRS reviews (microtasks) squeezed in between each phase.

Example

javascript
console.log('1: start');

setTimeout(() => console.log('2: setTimeout'), 0);

setImmediate(() => console.log('3: setImmediate'));

Promise.resolve().then(() => console.log('4: promise microtask'));

process.nextTick(() => console.log('5: nextTick'));

console.log('6: end');

Output

bash
1: start
6: end
5: nextTick
4: promise microtask
2: setTimeout
3: setImmediate

Explanation

Synchronous code (lines 1 and 6) runs first because the call stack is processed before any queue. Once the call stack is empty, process.nextTick callbacks run first, followed by the Promise microtask queue — both are drained completely before the event loop moves into its phases. Then the event loop enters the timers phase, running the setTimeout callback, and later the check phase runs the setImmediate callback. The exact ordering of setTimeout(0) versus setImmediate can vary when called from the main module, but inside an I/O callback, setImmediate always fires before setTimeout.

🏏

Cricket analogy: The toss and team announcement (sync code) happen first; then any urgent DRS review from the previous ball (nextTick/microtasks) is resolved before the umpire moves to signaling overs (timers) and eventually to boundary review checks (setImmediate) at the innings break.

Key Takeaways

  • Node.js runs JavaScript on a single main thread but delegates I/O to the OS and a libuv thread pool.
  • The event loop has ordered phases: timers, pending callbacks, poll, check, and close callbacks.
  • process.nextTick and Promise microtasks are drained after every callback, before the loop moves to the next phase.
  • process.nextTick callbacks always run before Promise microtasks.
  • Blocking the call stack with heavy synchronous computation freezes the entire event loop.

Practice what you learned

Was this page helpful?

Topics covered

#NodeJs#NodeJsExpressStudyNotes#WebDevelopment#NodeJsArchitectureAndTheEventLoop#Node#Architecture#Event#Loop#Loops#StudyNotes#SkillVeris