Skip to main content

Command Palette

Search for a command to run...

How Node.js Handles Multiple Requests with a Single Thread

Updated
3 min readView as Markdown

When people first hear that Node.js is "single-threaded," they wonder how it can handle thousands of requests simultaneously. Doesn't one slow request block everything? The answer is in how Node.js actually works—it's not doing one thing at a time the way you might think.

Thread vs Process: The Simple Version

A thread is like a worker doing a task. A process is a whole program running. Traditional web servers often use multiple threads—one thread per request. If you have 1000 concurrent requests, you need 1000 threads.

Node.js takes a different approach: one thread handles everything. That sounds limiting, but it's actually a superpower.

The Chef Analogy

Imagine a restaurant kitchen with one chef.

Traditional approach (multi-threaded): Each order gets its own chef. 100 orders = 100 chefs. Expensive, hard to coordinate.

Node.js approach: One chef takes orders, starts cooking, then takes the next order while the first one simmers. The chef never waits idly.

The chef is the event loop. The simmering food is a background task (like reading a file or querying a database). The chef doesn't stand around watching the pot—he moves on and comes back when it's done.

Node.js Architecture

Here's what actually happens when a request comes in:

  1. Request arrives → Event loop receives it

  2. Fast operations (JSON parsing, routing) → Handled directly

  3. Slow operations (database, file reading, API calls) → Delegated to background threads

  4. Event loop continues → Handles other requests while waiting

  5. Background task completes → Callback fires, response sent

app.get('/user/:id', (req, res) => {
  // 1. Fast - routing happens immediately
  // 2. Slow - database query goes to worker thread
  db.query('SELECT * FROM users WHERE id = ?', [req.params.id], (err, data) => {
    // 3. Callback runs when query finishes
    res.json(data);
  });
});

The event loop doesn't wait. It registers the callback and moves to the next request.

Handling Multiple Requests

Let's trace what happens with two simultaneous requests:

Time →
Request A: [route]──[query DB]──────────────[response]
Request B:          [route]──[query DB]──[response]

Event loop: never blocked, switches between tasks

Both requests make progress without blocking each other. That's concurrency—handling multiple tasks by switching between them, not doing them simultaneously.

Why Node.js Scales Well

Node.js doesn't use more threads as traffic increases. It just cycles through requests more efficiently. This works great when:

  • Most of your work is I/O-bound (network calls, disk reads, API requests)

  • You need real-time features (WebSockets, live updates)

  • You're building APIs that talk to many services

It struggles when you have CPU-heavy work—image processing, heavy calculations—because those block the single thread. For those cases, you'd offload to worker threads or separate services.

Wrapping Up

Node.js is single-threaded in the sense that one thread runs your code. But it's not doing nothing while waiting for I/O—it's handling other requests. That's what makes it fast for I/O-heavy applications.

Think of it this way: the thread is never idle unless there's genuinely nothing to do. The event loop is a relentless multitasker, switching between active work and waiting callbacks. That's the secret to handling thousands of requests with one thread.