Home Engineering

Is Node.js Really Single-Threaded?

23 June 2026 · 3 min read
Table of contents

“Node.js is single-threaded.” You’ve probably heard this. It’s true. And also kind of misleading.

If it only has one thread, how does it handle thousands of connections?


What most people assume

Coming from Java or Python, developers expect:

  • Request 1 gets Thread 1
  • Request 2 gets Thread 2
  • Request 3 gets Thread 3

One thread means one request at a time. That would make Node.js useless for any real server. But that’s not how it works.


What actually happens

There is one main thread running your JavaScript. When it hits something slow (a database query, an API call), it delegates that work and immediately moves on to the next request. When the work finishes, a callback brings the result back.

The piece that coordinates all of this is called the Event Loop.

Drag · Scroll to zoom

What this looks like in practice with an Express route:

app.get("/users", async (req, res) => {
  const users = await db.query("SELECT * FROM users");
  res.json(users);
});
  1. Request arrives, Event Loop picks it up
  2. JavaScript runs until db.query(...) is hit
  3. Query is delegated, main thread is free immediately
  4. Next request starts processing
  5. Database responds, callback fires
  6. res.json(users) runs, response sent

The main thread never waited. It was handling other requests the whole time.


Why this scales

Most web apps spend most of their time waiting:

  • Waiting for a database to return rows
  • Waiting for an external API to respond
  • Waiting for a file to be read
  • Waiting for a cache to reply

None of that waiting uses the CPU. The Event Loop fills that idle time with other requests. One thread, thousands of connections, because most of those connections are just waiting.


The nuance people miss

“JavaScript is single-threaded” is not the same as “Node.js has only one thread.”

  • Your JavaScript code: one main thread
  • File and DNS work: a background thread pool (libuv)
  • Networking: handled directly by the OS

You never manage those background threads. But they exist and do real work. The runtime is doing more than it looks like from your code.


Where it breaks down

The model falls apart with CPU-bound work:

for (let i = 0; i < 10_000_000_000; i++) {
  // heavy calculation
}

While this runs, the Event Loop is blocked. No other requests get through. Node.js is a poor fit for:

  • Video encoding
  • Image processing
  • Heavy data analysis

For CPU-bound work, use Worker Threads to move the computation off the main thread.