Why is Node.js considered single-threaded?
What "single-threaded" means
When people say Node.js is single-threaded, they mean:
There is one main thread that runs JavaScript code in Node.js. That thread is exactly where the event loop lives, handling all callbacks, promises and events.
In other words:
- Node.js does not have many threads by default, unlike Java, C++ or Python.
- Your JS code runs in one main thread, sequentially.
How it works under the hood
Node.js is built on the V8 engine (which runs JS) and the libuv library (which manages asynchrony and threads).
Schematically:
┌────────────────────────────────┐
│ Your JavaScript code (1 thread)│ ← Event Loop
└────────────────────────────────┘
↓
┌───────────────────┐
│ libuv ThreadPool│ ← background work (I/O)
└───────────────────┘The core idea:
- JS code runs in a single thread.
- When you call an async function (for example,
fs.readFile), Node.js delegates the heavy operation to the background thread pool, instead of blocking the main thread. - When the operation finishes, its result comes back to the event loop, and a callback or promise fires.
Example
const fs = require('fs');
console.log('Start');
fs.readFile('data.txt', 'utf8', (err, data) => {
console.log('File read');
});
console.log('End');Output order:
Start
End
File readWhy is that?
- JS runs in a single thread.
fs.readFilegoes to a libuv background thread.- The main thread does not wait and keeps running.
But Node.js does have other threads
Yes, there are additional threads inside Node.js, but they do not run JavaScript directly.
| Component | Threads | What it does |
|---|---|---|
| Event Loop | 1 | Runs JS code, handles events |
| libuv Thread Pool | 4 by default | Runs I/O operations (files, DNS, crypto) |
| System threads | many | OS, network operations, kernel |
The pool size can be changed:
UV_THREADPOOL_SIZE=8 node app.jsWhy this matters
Node.js does not use multithreading to run JS code, but it does use extra threads for background operations.
So:
- It is single-threaded from the point of view of running JavaScript.
- But it is multi-threaded under the hood for I/O and system tasks.
Why this is convenient
- A simple concurrency model No mutexes, no synchronization headaches, no locks to manage.
- Asynchrony instead of threads Instead of many threads, it uses the event loop and non-blocking I/O.
- Scalability Thanks to the non-blocking architecture, Node.js handles thousands of requests on a single thread.
But you can still use multithreading
If you need to spread computation across cores, Node.js offers:
| Approach | Module | Description |
|---|---|---|
| Cluster API | cluster | Runs multiple Node.js processes (one per CPU core) |
| Worker Threads | worker_threads | Lets you run JS code on separate threads |
| child_process | fork, spawn, exec | Runs external processes |
A Worker Threads example:
const { Worker } = require('worker_threads');
new Worker('./worker.js'); // a separate thread for heavy computationSummary
| Characteristic | Node.js |
|---|---|
| Thread for JS code | One (the event loop) |
| Threads under the hood | Yes (in libuv) |
| Parallel JS execution | No |
| Asynchrony | Via the event loop |
| Multithreading available manually | Yes (via worker_threads, cluster) |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.