Skip to main content

Command Palette

Search for a command to run...

How Node.js Handles Multiple Requests with a Single Thread

Updated
14 min read
How Node.js Handles Multiple Requests with a Single Thread
M

Software Developer

It's a busy Sunday evening in Patna.

You own a popular restaurant called Mudassir's Bistro. The restaurant is famous for its Butter Chicken, Biryani, and special Masala Chai. Because of its popularity, customers keep coming throughout the day.

Today is even busier than usual.

There are customers dining inside the restaurant. Orders are arriving through Zomato and Swiggy. The phone keeps ringing because people want home delivery. The waiter is shouting new orders, and dishes are leaving the kitchen one after another.

At this moment, your best friend Nishar walks in.

He looks around the restaurant carefully and suddenly becomes confused.

"Wait a minute, Mudassir bhai."

"I can see twenty customers."

"Some people are waiting for food. Some are placing new orders. Someone is asking for dessert."

"But there is only one chef in the kitchen."

He points towards you.

"You are the only chef."

"How are you managing everything at the same time?"

You smile.

"This question is much bigger than you think, Nishar."

"And the answer to this question explains one of the most interesting things about Node.js."

Most beginners hear this sentence:

Node.js is single-threaded.

As soon as they hear this, they assume:

"If Node.js has only one thread, then it must process one request at a time."

"If one request takes five seconds, every other request must wait."

"If thousands of users connect simultaneously, the server will crash."

Surprisingly, none of these assumptions are true.

Node.js powers chat applications, streaming platforms, APIs, live dashboards, gaming servers, and applications used by millions of users around the world.

So the obvious question is:

How can a single-threaded runtime handle thousands of client requests efficiently?

Does Node.js secretly create more threads?

Does it process requests in parallel?

Or is there some hidden mechanism working behind the scenes?

In this article, we will answer all these questions.

We will understand:

  • What "single-threaded" actually means.

  • Difference between a thread and a process.

  • How the Event Loop enables concurrency.

  • How Node.js delegates slow tasks to background workers.

  • How multiple client requests are handled.

  • Why Node.js scales so well.

  • Why concurrency is different from parallelism.

Nishar pulls a chair, orders a cold coffee, and says,

"Alright bhai, explain everything from the beginning."

You smile.

"Let's start with the most confusing terms first."

Thread vs Process — Understanding the Restaurant

Whenever developers start learning Node.js, they often hear two words:

  • Process

  • Thread

These words sound complicated, but they become very easy when you think about our restaurant.

Imagine Mudassir's Bistro as an entire business.

The restaurant has:

  • A kitchen

  • Tables

  • Inventory

  • Recipes

  • Electricity

  • Waiters

  • Cash counter

All these resources together form one complete system.

This complete system is similar to a Process.

In simple words,

A process is a running program with its own memory and resources.

Now think about the people working inside the restaurant.

There is:

  • One chef cooking food.

  • One waiter taking orders.

  • One cashier handling payments.

Each worker performs a specific task.

These workers are similar to Threads.

A thread is simply:

A unit of execution inside a process.

This means:

Restaurant = Process

Chef      = Thread
Waiter    = Thread
Cashier   = Thread

One process can have multiple threads working inside it.

Many traditional web servers follow a model called:

One Thread Per Request

Imagine if your restaurant created a new chef every time a customer entered.

Customer 1 arrives.

Create Chef 1.

Customer 2 arrives.

Create Chef 2.

Customer 100 arrives.

Create Chef 100.

This approach works for a small number of customers, but creating hundreds or thousands of chefs is expensive.

More chefs mean:

  • More memory usage.

  • More CPU scheduling.

  • More context switching.

  • More overhead.

Eventually, the restaurant becomes inefficient.

Nishar nods.

"So traditional servers create many threads."

"But Node.js is different, right?"

You smile.

"Exactly."

"Node.js follows a completely different philosophy."

Single-Threaded Nature of Node.js

Node.js executes JavaScript using a single main thread.

This means:

  • There is one call stack.

  • One JavaScript thread.

  • One Event Loop.

At any given moment, only one piece of JavaScript code is executed.

This is why people say:

Node.js is single-threaded.

But the sentence is incomplete.

The correct statement is:

Node.js executes JavaScript on a single thread but uses asynchronous mechanisms to handle many operations concurrently.

Let's go back to the restaurant.

Suppose you are the only chef.

You can chop vegetables.

You can cook curry.

You can prepare coffee.

But you cannot physically do all these things at the exact same moment.

You are only one person.

Similarly, the JavaScript thread can execute only one instruction at a time.

Nishar immediately becomes worried.

"Wait."

"If there is only one thread, then what happens when a customer asks for something that takes a long time?"

For example:

  • Reading a large file.

  • Querying a database.

  • Making an API request.

  • Encrypting a password.

"If the chef starts baking a cake for thirty minutes, won't every other customer have to wait?"

You laugh.

"That's exactly the problem Node.js was designed to solve."

A smart chef never does everything himself.

Suppose a customer orders freshly baked bread.

The bread needs twenty minutes in the oven.

Will you stand beside the oven and stare at it for twenty minutes?

Of course not.

You give the bread to the baker.

While the bread is baking, you:

  • Take new orders.

  • Serve other customers.

  • Check prepared dishes.

  • Coordinate the kitchen.

When the bread is ready, the baker informs you.

You pick it up and serve it.

Node.js follows the same principle.

When Node.js encounters operations like:

  • File system operations (fs.readFile)

  • DNS lookups

  • Cryptographic functions

  • Compression

  • Database or network operations

it does not block the JavaScript thread.

Instead, it delegates these operations to:

  • The Operating System

  • Background worker threads managed by libuv

Meanwhile, the main thread becomes free again.

It continues serving other requests.

Nishar smiles.

"So the chef isn't doing everything himself."

"He is coordinating everything."

"Exactly," you reply.

"And the person coordinating everything in Node.js is called the Event Loop."

The Event Loop — The Restaurant Manager

Imagine your restaurant has a manager.

This manager does not cook food.

He does not serve customers.

He does not wash dishes.

His job is much simpler.

He constantly checks:

  • Has a new customer arrived?

  • Is any dish ready?

  • Did the baker finish baking?

  • Did the waiter bring a new order?

He keeps moving from one task to another.

This manager is similar to the Event Loop.

The Event Loop is the heart of Node.js.

Its responsibility is:

  1. Receive incoming requests.

  2. Execute synchronous code.

  3. Delegate slow tasks to the OS or worker threads.

  4. Wait for completed operations.

  5. Execute their callbacks.

  6. Repeat the cycle forever.

You can visualize it like this:

Client Request
       ↓
Main Thread
       ↓
Is operation slow?
   /          \
 No            Yes
 |              |
Execute      Send to Worker/OS
 |              |
Done        Operation completes
                 |
          Event Loop receives callback
                 |
         Execute callback
                 |
          Send response

The Event Loop keeps rotating continuously.

It never blocks itself waiting for slow operations.

This is one of the biggest reasons Node.js can handle a large number of requests efficiently.

Delegating Tasks to Background Workers

Nishar takes a sip of coffee and asks,

"So who actually performs these slow tasks?"

You point towards the bakery section of your restaurant.

"Suppose someone orders bread."

You don't bake it yourself.

You give it to the baker.

Similarly, Node.js delegates slow operations to background workers.

These workers are managed by a library called:

libuv

libuv provides:

  • Event Loop

  • Thread Pool

  • Asynchronous I/O

  • OS-level abstractions

For tasks like:

  • File system operations

  • Cryptography

  • Compression

  • DNS lookups

libuv uses a thread pool.

By default:

UV_THREADPOOL_SIZE=4

This means Node.js has four worker threads available in the background.

The JavaScript thread remains free.

The worker performs the heavy operation.

Once the task completes,

the Event Loop is notified,

and the callback is executed.

This architecture allows Node.js to remain responsive even when slow operations are happening in the background.

Handling Multiple Client Requests

At this point, Nishar understands that Node.js uses a single JavaScript thread and delegates slow tasks to the background. However, he still has one important question.

He asks,

"Everything sounds good in theory, but I still don't understand how Node.js handles multiple users at the same time."

You smile.

"Let's use our restaurant again."

Suppose three customers arrive almost simultaneously.

Customer 1

The first customer says,

"I want to read a large file from the server."

In Node.js, this might look like:

const fs = require("fs");

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

Reading a file from disk is a slow operation.

Node.js knows that waiting for the file to be read would block the main thread.

Therefore, instead of performing the operation itself, Node.js sends this task to libuv's worker thread.

The main thread immediately becomes free.

Customer 2

At the same time, another customer makes a database request.

db.users.find();

This operation may take some time because the database needs to process the query and send the results back.

Again, Node.js does not wait.

The request is sent to the database server, and the main thread moves on.

Customer 3

Now a third customer arrives and asks:

"Can you just return a simple response?"

res.send("Hello World");

This operation is extremely fast.

Since the main thread is free, Node.js immediately responds.

The third customer receives the response without waiting for Customer 1 or Customer 2 to finish.

Nishar becomes surprised.

"So Node.js doesn't process requests one by one?"

You shake your head.

"It processes JavaScript one piece at a time, but it allows multiple operations to remain in progress simultaneously."

This is called:

Concurrency

Node.js does not wait for one operation to finish before starting another.

Instead, it coordinates many operations efficiently.

You can visualize this flow like this:

Request A → File Read ───────┐
                             │
Request B → Database Query ──┼──► Background Processing
                             │
Request C → Quick Response ──┘
               │
               ▼
        Main Thread stays free
               │
               ▼
          Event Loop
               │
               ▼
        Send responses

This ability to manage many operations without blocking the main thread is the reason Node.js performs so well for I/O-heavy applications.

Concurrency vs Parallelism

Nishar looks thoughtful.

"So Node.js is doing many things at the same time."

"Doesn't that mean Node.js is parallel?"

You smile.

"This is where many developers get confused."

Concurrency and Parallelism are related concepts, but they are not the same.

Parallelism

Imagine your restaurant has four chefs.

One chef cooks Biryani.

One chef prepares Butter Chicken.

One chef makes desserts.

One chef prepares drinks.

All four chefs are working at the exact same moment.

This is called:

Parallelism

Multiple tasks are executing simultaneously.

Concurrency

Now imagine there is only one chef.

That chef:

  • Takes an order.

  • Gives bread to the baker.

  • Starts preparing curry.

  • Checks whether the bread is ready.

  • Serves another customer.

  • Returns to finish the curry.

Only one chef exists.

However, many orders are progressing at the same time.

This is:

Concurrency

Multiple tasks make progress together, even though they are not executing simultaneously.

Node.js primarily works using concurrency.

The JavaScript thread executes one task at a time.

However, because slow operations are delegated to the operating system or worker threads, many requests remain active simultaneously.

Nishar finally understands.

"So JavaScript itself is not running in parallel."

"But Node.js can still manage many operations concurrently."

You nod.

"Exactly."

Why Node.js Scales So Well

At this point, Nishar asks the most important question.

"If Node.js has only one JavaScript thread, why do companies use it for large-scale applications?"

You answer,

"Because creating and managing threads is expensive."

Imagine your restaurant hires a new chef for every customer.

If ten customers arrive, you hire ten chefs.

If a thousand customers arrive, you hire a thousand chefs.

Soon, the restaurant spends most of its time managing chefs instead of serving customers.

This is similar to traditional thread-per-request architectures.

More threads mean:

  • More memory consumption.

  • More CPU overhead.

  • More context switching.

  • More complexity.

Node.js avoids this problem.

Instead of creating thousands of threads, Node.js uses:

  • One JavaScript thread.

  • One Event Loop.

  • Background worker threads when required.

  • Non-blocking I/O.

Because of this architecture:

  • Memory consumption remains lower.

  • Context switching is reduced.

  • The server remains responsive.

  • Thousands of concurrent connections can be handled efficiently.

This is why Node.js performs exceptionally well for:

Real-Time Applications

Applications like chat systems require thousands of users to remain connected simultaneously.

Node.js handles these persistent connections efficiently.

APIs and Microservices

Most APIs spend their time:

  • Waiting for database responses.

  • Calling external services.

  • Reading or writing data.

These are I/O operations.

Since Node.js is optimized for non-blocking I/O, it performs extremely well.

Streaming Applications

Platforms that stream:

  • Videos

  • Music

  • Live events

benefit from Node.js because it can continuously send chunks of data without blocking the server.

Collaboration Tools

Applications like:

  • Online editors

  • Live dashboards

  • Multiplayer games

require fast communication between many connected clients.

Node.js handles such workloads efficiently because it is designed around concurrency.

Important Points to Remember

By now, Nishar has almost finished his coffee.

Before leaving, he takes out his notebook and writes down the most important points.

1. Node.js is Single-Threaded

JavaScript executes on a single main thread.

There is only one call stack and one Event Loop.

2. Node.js is Concurrent

Multiple operations can remain in progress simultaneously.

The main thread does not wait for slow operations.

3. Slow Tasks Are Delegated

Operations such as:

  • File system access

  • DNS lookups

  • Cryptography

  • Compression

are delegated to:

  • libuv worker threads

  • Operating system APIs

4. Event Loop Coordinates Everything

The Event Loop:

  • Receives completed operations.

  • Executes callbacks.

  • Processes queued tasks.

  • Keeps the application responsive.

5. Node.js Scales Because of Non-Blocking I/O

Instead of creating one thread per request,

Node.js uses:

  • A single JavaScript thread

  • Event Loop

  • Background workers

  • Asynchronous I/O

This architecture allows Node.js to efficiently serve thousands of concurrent requests.

Conclusion

The restaurant is about to close.

The last customer leaves.

Nishar looks around once again.

Earlier in the evening, he thought it was impossible for a single chef to manage so many customers.

Now he understands the secret.

The chef was never trying to do everything himself.

He was coordinating the kitchen intelligently.

He delegated slow tasks.

He continued serving customers while others were working in the background.

And he returned only when the results were ready.

Node.js follows the same philosophy.

It executes JavaScript on a single thread, but it does not block itself waiting for slow operations.

Instead, it uses the Event Loop, asynchronous I/O, and background workers to coordinate thousands of operations efficiently.

That is why a single-threaded Node.js server can handle an enormous number of concurrent requests.

And that is why understanding the Event Loop is not just an interview topic.

It is the key to understanding how Node.js actually works behind the scenes.

Nishar smiles.

"So Node.js is not powerful because it has many threads."

"It is powerful because it knows when not to use them."

You laugh.

"And that's the secret recipe of Node.js."

#javascript #nodejs #eventloop #libuv #backend #webdevelopment #chaicode #hashnode