Blocking vs Non-Blocking Code in Node.js

Introduction:
Imagine you’re at a tea stall:
Blocking style → The shopkeeper serves only one customer at a time. Everyone else must wait.
Non-blocking style → The shopkeeper takes multiple orders quickly and prepares them as resources become available.
Node.js works like the second case: it doesn't stop everything for one task.
In this blog, we’ll dive into why Node.js remains a top choice for high-performance applications by mastering the fundamental shift from blocking execution to a non-blocking, event-driven architecture.
What Blocking Code Means
Blocking occurs when the execution of additional JavaScript in the Node.js process must wait until a non-JavaScript operation completes. This happens because Node.js is single-threaded. When you run a heavy synchronous task, you "block" the Event Loop.
Blocking code refers to operations that halt the execution of further code until the current task completes.
- Impact: If a file takes 5 seconds to read, your entire server is "dead" for those 5 seconds. No other users can even load the homepage.
Example: Synchronous File Read
const fs = require('fs');
// The code stops here until the file is fully read
const data = fs.readFileSync('/file.md');
console.log(data);
console.log("This won't run until the file is finished!");
What Happens:
Node.js waits until the file is completely read
No other request can be handled during this time
What Non-Blocking Code Means
Non-blocking code allows the execution of the next lines of code without waiting for the current operation to finish. These are usually Asynchronous operations that are offloaded to the system kernel or a thread pool.
Non-blocking code allows execution to continue without waiting for a task to finish.
- Impact: The server stays responsive. It initiates a task, moves on, and handles the result whenever it's ready.
Example: Asynchronous File Read
const fs = require('fs');
// Node starts this task and immediately moves to the next line
fs.readFile('/file.md', (err, data) => {
if (err) throw err;
console.log("File content ready!");
});
console.log("I run immediately, even before the file is read!");
What Happens:
File reading happens in the background
Execution continues instantly
Callback runs when file is ready
Why blocking slows servers
When a server uses blocking code:
Each request waits in line
CPU stays idle while waiting for I/O
Throughput decreases
Node.js uses a single thread to handle all requests. To visualize why blocking is a "server killer," look at this timeline:
Diagram: Blocking vs. Non-Blocking
Blocking Scenario (The Traffic Jam)
Request A (DB Query) |████████████████████| -> Done
Request B | | (Waiting...)
Request C | | (Waiting...)
^ All other users are stuck until A finishes.
Non-Blocking Scenario (The Fast Lane)
Request A (DB Query) |█ (Offloaded) | ... -> Callback Triggered
Request B (Static) |██| -> Done
Request C (Static) |██| -> Done
^ Server keeps working while A is processed in background.
Async Operations in Node.js
Most "heavy lifting" in Node.js is non-blocking by default. These include:
I/O Operations: Reading/Writing to files or the network.
Database Calls: Querying MongoDB, PostgreSQL, etc.
Timers:
setTimeoutorsetInterval.
When these operations are called, Node.js sends the task to Libuv (a C++ library), which handles the task in the background. Once finished, it pushes a callback onto the Task Queue, and the Event Loop eventually picks it up.
Example (Async with setTimeout)
console.log("Start");
setTimeout(() => {
console.log("Async Task Done");
}, 2000);
console.log("End");
Output:
Start
End
Async Task Done
Shows non-blocking behavior clearly
Real-World Examples
1. File Reading
readFileSync()→ Blocking ❌readFile()→ Non-blocking ✅
2. Database Call
db.getUser(id, (user) => {
console.log(user);
});
While DB fetch happens:
- Server can handle other users
3. API Request
fetch("https://api.example.com/data")
.then(res => res.json())
.then(data => console.log(data));
Network delay ≠ server freeze
Quick Comparison: Blocking vs Non-Blocking
| Feature | Blocking | Non-Blocking |
|---|---|---|
| Execution | Synchronous | Asynchronous |
| Wait behavior | Stops execution | Continues execution |
| Performance | Slow under load | Scalable |
| Node.js usage | Rare | Preferred |
Key Takeaways
Node.js is single-threaded but non-blocking
Blocking code freezes the event loop
Non-blocking uses callbacks, promises, async/await
Best performance comes from avoiding synchronous I/O
Always prefer async APIs in production systems
In closing
I hope that you’ve found this blog on “Blocking vs Non-Blocking Code in Node.js” helpful...!
That's all for today! 😁 You reached the end of the article 😍.
Want more..?
I write articles on princekumar-engineer.hashnode.dev, and also post development-related content on the following platforms:




