Chapter 7: Node.js Event Loop

Node.js Event Loop — written as if I’m sitting next to you with a whiteboard, drawing phases, arrows, and little code snippets while explaining what really happens under the hood.

We will go slowly, step by step, with visuals in text form, real examples, and the most common misunderstandings people have.

1. The most important sentence you must remember

The Event Loop is what makes Node.js feel “non-blocking” and allows it to handle thousands of connections with only one JavaScript thread.

Node.js is single-threaded for JavaScript execution, but asynchronous thanks to the event loop + libuv.

Without the event loop → Node.js would behave like old-school PHP or blocking Python → one request at a time.

2. Very simplified mental model (first version)

Imagine you are a very fast waiter in a huge restaurant:

  • You have only one brain (single JavaScript thread)
  • You can take orders instantly (accept new connections)
  • You give the order to the kitchen (file system, database, network…)
  • While the kitchen is cooking → you immediately go to the next table
  • When the kitchen rings the bell → you pick up the food and serve it

The event loop is your brain constantly checking:

“Do I have any finished dishes / timers / new customers right now?”

That checking loop never stops.

3. The real phases of the Node.js Event Loop

text

Every time the event loop finishes one full cycle → it’s called one tick.

Most real work happens in phases 1, 4 and 5.

4. Detailed explanation of each phase

Phase 1 – Timers

Executes callbacks of expired setTimeout and setInterval.

JavaScript

Output:

text

→ even with 0 ms → timer callbacks never run immediately

Phase 4 – Poll (the heart of Node.js)

This phase does two main things:

  1. Retrieves new I/O events that are ready (finished file reads, incoming HTTP data, network responses…)
  2. Executes their callbacks — if there are any

If there is nothing to do in poll → Node.js will wait here (unless there are timers or setImmediate waiting)

Very important rule:

If the event loop is only waiting in the poll phase → Node.js keeps the process alive If there is nothing left to do (no timers, no pending I/O, no setImmediate) → process exits

Phase 5 – Check

Executes setImmediate callbacks.

JavaScript

Very common result (especially on Linux/macOS):

text

→ setImmediate often wins over setTimeout(…, 0) when run inside I/O callbacks

5. Classic examples everyone should run once

Example 1 – Timers vs setImmediate

JavaScript

Possible outputs:

text

or

text

it depends on when the script started relative to the poll phase

Example 2 – Inside an I/O callback

JavaScript

Almost always prints:

text

→ because readFile callback runs in poll phase → setImmediate is next phase

Example 3 – process.nextTick (special case – NOT part of event loop phases)

JavaScript

Output:

text

→ process.nextTick runs immediately after current operation, before the event loop continues

→ it’s not part of the official phases → it’s a microtask queue (like Promises)

6. Microtasks vs Macrotasks (very important distinction)

Node.js has two kinds of async callbacks:

Type Examples When do they run? Priority
Microtasks process.nextTick, Promise.then After current operation, before next phase Highest
Macrotasks setTimeout, setImmediate, I/O In the proper event loop phase Lower

Order in one tick:

text

7. Summary table – Quick reference

Phase / Mechanism Typical callbacks Runs when? Can starve the loop?
process.nextTick Microtasks After current operation Yes – very dangerous
Timers setTimeout, setInterval When timer expires No
Poll I/O callbacks (fs, net, http…) When I/O events are ready No
Check setImmediate After poll phase No
Close callbacks socket.on(‘close’), server.on(‘close’) When resources close No

8. Practical rules of thumb (2025–2026 style)

  • Use setImmediate when you want something to happen after current I/O, but before next timer
  • Use process.nextTickvery sparingly — only when you really need something to happen right now (dangerous in loops)
  • Prefer await / Promise over callbacks — cleaner and microtask-based
  • Never put heavy synchronous CPU work in hot paths — it blocks the entire loop
  • For long CPU tasks → use Worker Threads (they have their own event loop)

Want to go deeper into any part?

Popular next steps people usually ask:

  • How Promises and async/await interact with the event loop
  • Real debugging: printing phase names to understand order
  • What happens with setTimeout(…, 0) vs setImmediate in production
  • How Worker Threads have their own event loops
  • Common starvation patterns and how to avoid them
  • Visualizing event loop with clinic.js or async_hooks

Tell me which direction you want — I’ll continue with more code examples and concrete scenarios. 😄

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *