Skip to solution
hardSystem Design

How do you achieve concurrency in Node.js since it's single-threaded?

660 views
01

Understand the problem

Question presented to candidate: "Your Node server handles 5 requests at the same time, each needing to wait 300ms on a downstream call. Does the 5th request wait for the first 4 to finish first, or do they all complete in roughly 300ms total?"

What a strong answer should cover:

  • Node's JavaScript runs on one thread, but concurrency comes from never blocking that thread on I/O — when a request needs to wait (a downstream call, a database query, a timer), the thread is freed to serve other requests during that wait, rather than sitting idle until the first one resolves.
  • 📌 Verified, not just asserted: 5 concurrent requests to a real HTTP server, each with a non-blocking 300ms delay, completed all 5 in ~356ms of total wall time — not the ~1500ms a naive, one-request-at-a-time (or blocking-per-request) server would need for the same workload.
  • The actual mechanism has two layers: the event loop (covered in its own dedicated question) schedules callbacks without ever parking the thread on a wait; and libuv (covered in its own dedicated question) provides the underlying async I/O primitives — the OS's native async networking facilities for sockets, plus a thread pool for a handful of specific blocking operations (file I/O, some crypto, DNS).
  • 📌 The critical caveat, stated precisely: this concurrency model helps I/O-bound waiting specifically. It does not parallelize CPU-bound work — a genuinely CPU-heavy computation inside one request handler still blocks the single thread, and therefore every other concurrent request, exactly as verified with real heartbeat-timer measurements in the dedicated blocking-vs-non-blocking and event-loop-pollution questions.
  • A precise answer distinguishes this from true parallelism: Node's single-thread concurrency model overlaps waiting time across many requests; it does not run multiple requests' JavaScript simultaneously on separate CPU cores. Actual multi-core parallelism needs the cluster module or additional processes (covered in its own dedicated question) for I/O-bound scaling, or Worker Threads for CPU-bound work.
  • The direct, concrete answer to the prompt's scenario: all 5 requests complete in roughly 300ms total, not 1500ms — provided the 300ms wait is genuinely non-blocking (a timer, a real async I/O call), which is exactly what was measured.

Clarifying questions expected:

  • "Is the 300ms wait a genuinely non-blocking operation (a timer, async I/O), or a synchronous computation that happens to take 300ms?" — only the former achieves the overlap being asked about.
  • "Is the concern I/O-bound concurrency specifically, or CPU-bound parallelism?" — Node's single-thread model addresses the former, not the latter.

Code / implementation expected: Yes — the real, measured 5-concurrent-requests result (~356ms, not ~1500ms) is the concrete, convincing proof, not a description of "non-blocking I/O."

concurrencysingle-threadedevent loopworker threads
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js system-design interviews — assumes basic event-loop familiarity. Difficulty: Hard

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The 5-concurrent-request measurement below was **actua

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real HTTP server: 5 concurrent, genuinely non-blocking requests complete in ~356ms total, not ~1500ms
const http = require("http");

const server = http.createServer((req, res) => {
  setTimeout(() => res.end("slow response after 300ms"), 300); // non-blocking
});

server.listen(0, async () => {
  const port = server.address().port;
  const t0 = Date.now();
  await Promise.all(
    Array.from({ length: 5 }, () => fetch(`http://127.0.0.1:${port}/`).then((r) => r.text()))
  );
  console.log("5 concurrent 300ms-delayed requests, total time:", Date.now() - t0, "ms");
  // 5 concurrent 300ms-delayed requests, total time: 356 ms
  // (sequential serving of the same 5 requests would take ~1500ms)
  server.close();
});
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 131 of 152 decoded in the Node.js track. One more won't hurt.

Back to track