DEV Community

Akash Gupta
Akash Gupta

Posted on

The Node.js Event Loop, Explained Simply (with Examples)

"Node.js is single-threaded" — you've heard it a hundred times. So how does it handle thousands of requests at once without freezing?

The answer is the event loop. Once it clicks, a lot of Node "magic" suddenly makes sense. Let's break it down.

The one-line mental model

Node runs your JavaScript on one main thread, but hands off slow work (file reads, network calls, timers) to the system, and picks up the results later via the event loop. So one thread juggles thousands of connections — it just never sits around waiting.

Blocking vs non-blocking

// ❌ Blocking — nothing else runs until this file is read
const data = fs.readFileSync('big.txt')

// ✅ Non-blocking — Node starts the read and moves on
fs.readFile('big.txt', (err, data) => {
  console.log('done reading')
})
console.log('this prints FIRST')
Enter fullscreen mode Exit fullscreen mode

Output:

this prints FIRST
done reading
Enter fullscreen mode Exit fullscreen mode

The second version doesn't wait — that's the whole point.

The phases (simplified)

Each loop iteration goes through phases, in order:

  1. TimerssetTimeout / setInterval callbacks
  2. Poll — I/O callbacks (file, network)
  3. ChecksetImmediate callbacks
  4. Close — cleanup callbacks

And between every phase, Node drains the microtask queue first.

Microtasks jump the line

process.nextTick() and resolved Promises are microtasks — they run before the loop moves to the next phase.

console.log('1')
setTimeout(() => console.log('2'), 0)
Promise.resolve().then(() => console.log('3'))
console.log('4')
Enter fullscreen mode Exit fullscreen mode

Output:

1
4
3   ← Promise (microtask) runs before setTimeout
2
Enter fullscreen mode Exit fullscreen mode

Most people guess 1 2 4 3. If you understand why it's 1 4 3 2, you understand the event loop.

setTimeout vs setImmediate

Inside an I/O callback, setImmediate always fires before setTimeout(fn, 0):

fs.readFile('f.txt', () => {
  setTimeout(() => console.log('timeout'), 0)
  setImmediate(() => console.log('immediate'))
})
// → immediate, then timeout
Enter fullscreen mode Exit fullscreen mode

The one rule that keeps Node fast

Never block the event loop. A heavy CPU task (huge loop, sync crypto, giant JSON parse) freezes everything, because it hogs the single thread. Offload it:

  • worker_threads for CPU-heavy work
  • the cluster module to use all cores
  • a queue/microservice for big jobs

TL;DR

  • One JS thread + the event loop = non-blocking I/O
  • Microtasks (nextTick, Promises) run before the next phase
  • setImmediate beats setTimeout(0) inside I/O
  • Don't block the loop with heavy CPU work

The event loop is one of the most common interview questions too — if you want the full set, I put together a 64-question Node.js interview guide here: https://asbackendinstitute.com/blog/nodejs-interview-questions/top-60-nodejs-interview-questions


I teach hands-on backend development (Node.js, Express, MongoDB, Redis) at AS Backend Institute. More practical guides like this if you're learning backend. 🚀

Top comments (0)