Node.js › Node.js Fundamentals & Runtime Architecture
How does Node.js achieve high concurrency for I/O-bound workloads despite running JavaScript on a single main thread?
AdvancedAnswer
Node.js's single-threaded JavaScript execution model is combined with an event-driven, non-blocking I/O architecture -- when an I/O operation (a network request, a file read, a database query) is initiated, Node.js delegates the actual waiting to the underlying operating system (or libuv's thread pool for operations lacking native OS-level async support), immediately returning control to the event loop to handle other work, rather than blocking the single thread waiting for that I/O to complete; when the I/O eventually finishes, its callback is queued to run on the main thread. This lets a single Node.js process efficiently juggle thousands of concurrent connections, since it spends almost no time idly blocked waiting on any single one.
// This doesn't block the single thread -- Node.js moves on immediately
// to handle other requests while this database query is in flight
app.get('/users/:id', async (req, res) => {
const user = await db.users.findById(req.params.id); // non-blocking wait
res.json(user);
});
// Meanwhile, thousands of OTHER concurrent requests can be handled
// on this same single thread while this one awaits its database responseReal-world example A Node.js API handling 10,000 concurrent, mostly I/O-bound (database and API-call-heavy) requests per second runs efficiently on a single process with modest CPU usage, since the single thread spends almost all its time actually executing quick bursts of JavaScript between I/O waits rather than being blocked, unlike a traditional thread-per-request model that would need thousands of OS threads for the same concurrency level.