Skip to main content

How Node.js Handles Multiple Requests with a Single Thread

Updated 6 min readMarkdown

Introduction

If you come from languages like Java or C++, Node.js feels confusing at first.

One thread. Thousands of users. APIs responding instantly. Chats updating in real time.

Most beginners hear:

“Node.js is single-threaded.”

…and assume it can only handle one user at a time.

That is not true.

Node.js works differently from traditional backend systems. Instead of creating many threads for every request, it focuses on efficient task handling using:

  • Non-blocking I/O

  • Event Loop

  • Background workers

  • Asynchronous execution

The result is a backend runtime capable of handling massive numbers of concurrent requests efficiently.


Single-Threaded Nature of Node.js

Node.js mainly runs JavaScript on a single main thread.

That means:

  • One thread executes JavaScript code

  • Tasks are processed sequentially

  • Only one piece of JavaScript runs at a time

This sounds limiting at first.

But the important detail is:

Node.js avoids waiting.

Traditional servers often waste time waiting for:

  • Database responses

  • File reads

  • API calls

  • Network operations

Node.js delegates these operations instead of blocking the main thread.


Thread vs Process

Before understanding Node.js properly, it is important to understand the difference between a process and a thread.

Process

A process is an independent running application.

Examples:

  • Chrome

  • VS Code

  • Spotify

Each process has its own memory space.


Thread

A thread is a worker inside a process.

Think of it like this:

  • Process = Restaurant

  • Threads = Workers inside the restaurant

Traditional backend systems create many threads.

Node.js mainly uses one main thread for JavaScript execution.


Chef Handling Orders Analogy

Imagine a restaurant.

Traditional Blocking Server

A chef takes one order.

Customer says:

“Make biryani.”

The chef:

  • Starts cooking

  • Waits for rice

  • Waits for ingredients

  • Waits for everything

Meanwhile, every other customer waits.

This approach does not scale efficiently.


Node.js Style

The chef takes an order.

Rice needs 20 minutes?

The chef says:

“Kitchen handle this. Next order.”

Then the chef immediately handles other customers.

When the rice is ready, the kitchen informs the chef.

The chef then finishes serving.

In Node.js:

  • Chef = Event Loop

  • Kitchen workers = Background workers / OS APIs

This is why Node.js performs extremely well for APIs and real-time applications.


How Node.js Handles Multiple Requests

Suppose multiple users hit the server at the same time.

app.get("/", async (req, res) => {
  const data = await fetchData();
  res.send(data);
});

Here is what happens internally.


Step 1: Request Arrives

Node.js receives the request.

The main thread processes it quickly.


Step 2: Slow Operation Detected

fetchData() may:

  • Read from database

  • Read a file

  • Call another API

These are slow I/O operations.

Instead of waiting, Node.js delegates them.


Step 3: Background Workers Handle the Task

The task is delegated to:

  • Operating System

  • libuv thread pool

  • External systems

Meanwhile, the main thread becomes free immediately.

It can now handle other incoming requests.


Step 4: Event Loop Checks Completion

Once the task finishes, the event loop notices it.

The callback or promise is pushed into the queue.

Then Node.js executes the callback and sends the response.


Event Loop and Concurrency

The event loop is the core mechanism behind Node.js concurrency.

Its job is to:

  • Monitor completed async tasks

  • Handle callbacks

  • Execute queued operations

  • Keep the server responsive

Simplified version:

while(true){
   checkCompletedTasks();
   executeCallbacks();
}

This is not the actual implementation, but it helps understand the concept.


Concurrency vs Parallelism

Many beginners confuse these terms.

Concurrency

Handling many tasks efficiently.

Example:

One chef managing multiple orders.

Node.js mainly uses concurrency.


Parallelism

Actually performing multiple computations simultaneously.

Example:

Multiple chefs cooking at the same time.

CPU-heavy systems rely on parallelism.


Node.js Emphasizes Concurrency

Node.js is excellent for:

  • APIs

  • Real-time chats

  • Streaming platforms

  • Dashboards

  • Live collaboration apps

Because most of these applications spend time waiting for I/O operations.

Node.js avoids blocking during this waiting period.


Why Node.js Scales Well

Traditional threaded servers:

  • Need memory for every thread

  • Require context switching

  • Increase CPU overhead

Node.js reduces these costs because:

  • Fewer threads are used

  • Non-blocking architecture improves efficiency

  • The event loop keeps the system responsive

This allows Node.js servers to handle very large numbers of concurrent connections.

Companies like Netflix and LinkedIn adopted Node.js for parts of their backend systems because of this scalability.


Important Limitation

Node.js is not ideal for CPU-heavy operations.

Example:

while(true){}

This blocks the single thread completely.

As a result:

  • No other requests can be handled

  • The server freezes

CPU-intensive tasks should use:

  • Worker Threads

  • Clustering

  • External services


Diagram Idea 1: Single Thread Handling Multiple Requests

Client Requests
      ↓
 Main Thread
(Event Loop)
      ↓
Delegates Slow Tasks
      ↓
 Background Workers
      ↓
Task Completion
      ↓
 Callback Queue
      ↓
 Responses Sent

Diagram Idea 2: Event Loop + Worker Thread Interaction

Request A ─┐
Request B ─┼──> Event Loop
Request C ─┘         │
                     ▼
           Background Workers
                     │
                     ▼
              Completed Tasks
                     │
                     ▼
                 Responses

Simple Async Example

const fs = require("fs");

console.log("Start");

fs.readFile("data.txt", "utf8", (err, data) => {
  console.log(data);
});

console.log("End");

Output

Start
End
[file content]

The file operation runs asynchronously.

Node.js does not wait for the file to finish reading.


Final Thoughts

Node.js handles multiple requests efficiently not by creating countless threads, but by minimizing waiting.

Its event loop delegates slow tasks, handles callbacks, and keeps the server responsive.

The real strength of Node.js is:

Efficient concurrency, not raw parallel execution.

That architectural decision made Node.js one of the most popular backend technologies for modern web applications.


Specifications (Deep Dive)

Core Components

Component Role
V8 Engine Executes JavaScript
Event Loop Manages async execution
libuv Handles async I/O and thread pool
Callback Queue Stores completed task callbacks
Worker Threads Handle CPU-heavy tasks

Tasks Delegated to Background Systems

OS Handled

  • Network requests

  • Socket operations

libuv Thread Pool

  • File system operations

  • DNS lookup

  • Some cryptography operations

Default thread pool size:

4

Can be increased using:

UV_THREADPOOL_SIZE=8

Event Loop Phases

Node.js event loop phases:

  1. Timers

  2. Pending callbacks

  3. Idle/prepare

  4. Poll

  5. Check

  6. Close callbacks

Understanding these phases becomes important when working deeply with:

  • Promises

  • Timers

  • Microtasks

  • Async behavior


1 view

No comments yet. Be the first to comment.