Node.js Event Loop Architecture — How a Single-Threaded Runtime Handles Massive Concurrency

Iniciado por joomlamz, 24 de Maio de 2026, 17:35

Respostas: 0   |   Visualizações: 18

Tópico anterior - Tópico seguinte

0 Membros e 1 Visitante estão a ver este tópico.

Node.js Event Loop Architecture — How a Single-Threaded Runtime Handles Massive Concurrency



Tópico: Node.js Event Loop Architecture — How a Single-Threaded Runtime Handles Massive Concurrency
Categoria: Tutoriais | Programação & Tecnologia
Idioma Principal: Português (Conteúdo de Tecnologia)

Descrição do Conteúdo / Informações:
-------------------------------------------------------------------------
When I first started working with Node.js, one thing didn't sit right with me:

How can a single-threaded system handle thousands of requests at the same time?

It sounds contradictory. But once I understood the event loop properly (especially from the official docs), everything clicked. This blog is my attempt to explain it in the simplest way possible, without losing depth.



What "Single-Threaded" Actually Means


Node.js is often called single-threaded, but that statement is incomplete.

• JavaScript execution runs on one main thread

• But Node.js itself is not limited to one operation at a time


It uses:

• OS kernel

• Background threads (libuv)

• Async I/O

→ So the correct statement is:

Node.js is single-threaded for JavaScript execution, but multi-system for handling I/O

This distinction is everything.



The Core Idea: Event-Driven, Non-Blocking Architecture


Node.js does not wait for tasks to complete.

Instead, it follows this pattern:

• Receive request

• Start the task (DB call, file read, API call)

• Do not wait

• Move to the next request

• Come back later when result is ready

This is called non-blocking I/O.

→ From official docs:

Node.js offloads operations to the system whenever possible, so the main thread stays free.



Think of It Like This (Simple Analogy)


Imagine:

• You are a waiter (event loop)

• Kitchen = OS / background workers

You:

• Take orders

• Pass them to kitchen

• Serve completed dishes

You don't:

• Cook yourself

• Wait idle for one order

That's exactly how Node.js scales.



Deep Dive: Event Loop Phases


The event loop is not just a queue. It runs in phases, each handling specific types of callbacks.



Architecture:




Main Phases:


• Timers

• Executes callbacks from setTimeout() and setInterval()

• Pending Callbacks

• Handles system-level callbacks (like TCP errors)

• Idle / Prepare

• Internal use (not something we deal with)

• Poll Phase (Most Important)

• Retrieves new I/O events

• Executes I/O callbacks

• Waits if nothing to do

• Check Phase

• Executes setImmediate() callbacks

• Close Callbacks

• Runs cleanup callbacks (like socket.on('close'))

┌───────────────────────────┐
│           timers          │
└─────────────┬─────────────┘

v
┌───────────────────────────┐
┌─>│     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │      close callbacks      │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤           timers          │
└───────────────────────────┘

→ The loop cycles through these continuously.



Why Poll Phase is the Heart


This is where the magic happens.

• Incoming requests get processed here

• Completed async tasks return here

• If nothing is pending → Node waits efficiently

→ This is why Node.js doesn't waste CPU cycles and stays highly scalable.



process.nextTick() vs setImmediate()


This is a small but important detail most people ignore.



process.nextTick()


• Executes immediately after current function

• Runs before event loop continues

• Can block I/O if abused



setImmediate()


• Executes in next iteration (check phase)

• Safer and more predictable

→ Official docs suggest:

Prefer setImmediate() in most real-world scenarios



How Node.js Handles Thousands of Requests


Now the real question.



Traditional Server (Thread-per-request)


• Each request = new thread

• Memory heavy

• Context switching overhead



Node.js Approach


• Single thread handles all requests

• No thread creation per request

• Uses async callbacks instead



What Actually Happens:


• 1000 users hit server


Node.js:

• Registers all requests

• Starts async operations

• Keeps event loop free



As responses come back:

• Callbacks are queued

• Event loop executes them

→ Result:

High concurrency with low resource usage



Important Insight: Node.js is Best for I/O-bound Work


Node.js shines when:

• DB queries

• API calls

• File system operations

• Streaming

• Real-time apps (chat, sockets)

But...

Not ideal for:

• Heavy CPU computations

• Large synchronous loops

Because:

Blocking the event loop = blocking everything



Common Mistakes That Kill Performance




1. Blocking Code


while(true) {}

→ Freezes entire server



2. Misusing process.nextTick()


• Can starve the event loop

• Prevents I/O execution



3. Writing Sync Code in APIs


fs.readFileSync()

→ Avoid in production



If You Need More Power (Scaling Beyond One Core)


Node.js is single-threaded per process, but you can scale using:

• Cluster module

• Worker threads

• Load balancers

→ This allows:

• Multi-core utilization

• Horizontal scaling



My Final Understanding


After going through the official Node.js docs and actually building apps, this is how I think about it:

• Node.js is not trying to do everything at once

• It is trying to never block

The event loop is just a smart coordinator:

• It runs what is ready

• Skips what is waiting

• Keeps the system moving

→ That's why:

Node.js can handle thousands of concurrent requests — not by parallel execution, but by efficient scheduling and non-blocking design



Conclusion


If I had to summarize in one line:

Node.js scales not because it is fast at doing work, but because it is excellent at not waiting unnecessarily

Once this mindset clicks, everything about Node.js architecture starts making sense.


Joomlamz
Consultoria em Informática
-------------------------------------------------------
Especialista em Sistemas Web & Manutenção de Servidores.
A desenvolver o novo AplPortal com suporte a PHP 8.
Precisa de ajuda profissional? Contacte-me.

Tags: