Single-Threaded, Non-Blocking
Node.js runs your JavaScript code on a single main thread — but it handles many concurrent operations (file reads, network requests) without blocking that thread, by delegating slow work elsewhere and using a callback-driven event loop to pick up results when ready.
Key Architecture Pieces
| Piece | Role |
|---|---|
| V8 | Compiles and executes JavaScript |
| libuv | A C library providing the event loop and a thread pool for I/O |
| Event Loop | Continuously checks for completed async work and runs its callbacks |
| Thread Pool | A small pool of background threads libuv uses for things like file system operations |
Non-Blocking in Action
Console Output
Click “Run” to see the console output here.
Why This Matters
A traditional multi-threaded server might spin up a new thread per request, which is memory-heavy at scale. Node.js instead handles thousands of concurrent connections on one thread, since most server work (waiting on a database or a network call) is I/O, not CPU — exactly what the non-blocking model is optimized for.
CPU-Heavy Work Blocks Everything
The non-blocking model only helps with I/O. A genuinely CPU-intensive task (like processing a huge image synchronously) still runs on the single main thread and blocks every other request until it finishes — this is Node.js's main weakness, addressed with worker threads for CPU-bound work.
Best Practice
Node.js is an excellent fit for I/O-heavy workloads (APIs, real-time apps) and a poor fit for CPU-heavy workloads (video encoding, heavy computation) on the main thread — know which kind of work your application actually does.