Skip to main content
Practice Problems

How does the event loop work in Node.js?

The Node.js Event Loop

The Event Loop is the heart of Node.js. It's what allows Node.js to perform non-blocking I/O despite being single-threaded β€” by offloading operations to the OS and libuv's thread pool, then executing callbacks when they complete.


Event Loop Phases

The event loop processes callbacks in a strict order of phases:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€>β”‚ timers β”‚ ← setTimeout, setInterval callbacks β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ pending callbacks β”‚ ← I/O callbacks deferred to next loop β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ idle, prepare β”‚ ← internal use only β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ poll β”‚ ← retrieve new I/O events β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ check β”‚ ← setImmediate callbacks β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” └─── close callbacks β”‚ ← socket.on('close', ...) β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Between each phase, Node.js runs microtasks:

  1. process.nextTick() callbacks (highest priority)
  2. Promise callbacks (.then(), async/await)

Execution Order Example

js
console.log('1: start'); setTimeout(() => console.log('4: setTimeout'), 0); setImmediate(() => console.log('5: setImmediate')); Promise.resolve().then(() => console.log('3: Promise')); process.nextTick(() => console.log('2: nextTick')); console.log('1: end'); // Output: // 1: start // 1: end // 2: nextTick // 3: Promise // 4: setTimeout // 5: setImmediate

Phase Details

1. Timers

Executes callbacks scheduled by setTimeout() and setInterval() whose delay threshold has passed.

2. Poll

Retrieves new I/O events. If nothing is queued, it waits here for I/O callbacks.

3. Check

Executes setImmediate() callbacks β€” always after I/O events.


Microtasks vs Macrotasks

TypeExamplesPriority
Microtaskprocess.nextTick, PromisesHighest β€” runs between every phase
MacrotasksetTimeout, setInterval, I/OLower β€” runs in event loop phases

Key Takeaway

The event loop allows Node.js to handle thousands of concurrent connections without creating a thread per connection. As long as you avoid blocking the main thread (e.g. with synchronous fs calls or CPU-heavy loops), Node.js remains highly performant.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems