Node.js Blocking vs Non-Blocking — Complete Guide to Event Loop Performance
In this tutorial, you will learn about Node.js Blocking vs Non. We cover key concepts, practical examples, and best practices to help you master this topic.
Node.js blocking operations halt the event loop until completion, while non-blocking operations delegate work and return immediately through callbacks, promises, or async-await.
What You'll Learn
By the end of this tutorial, you'll identify blocking and non-blocking APIs, measure event loop lag, convert blocking patterns to non-blocking, and optimize I/O-heavy applications.
Why Blocking Matters
Each blocking call freezes all concurrent requests. A single synchronous file read of 100ms delays every other user. Non-blocking design is essential for Node.js performance.
Real-World Use
An Express server with 1000 concurrent users must avoid sync file reads, JSON.parse on large payloads, and CPU-bound loops. Offloading these to worker threads or async APIs keeps responses fast.
Blocking Learning Path
flowchart LR
A[Event Loop] --> B[Blocking vs Non-Blocking]
B --> C[Async Patterns]
C --> D[Error Handling]
D --> E[Streams]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Synchronous Blocking Example
Synchronous methods like readFileSync block the event loop until the file is fully read. No other JavaScript executes during this time.
const fs = require("node:fs");
console.log("Start blocking read");
const data = fs.readFileSync("/etc/hostname", "utf8");
console.log("Data:", data.trim());
console.log("End blocking read");
// Other requests are queued during this entire period
Asynchronous Non-Blocking Example
Asynchronous methods like readFile return immediately. The callback runs when the result is ready, allowing other code to execute in the meantime.
const fs = require("node:fs");
console.log("Start non-blocking read");
fs.readFile("/etc/hostname", "utf8", (err, data) => {
if (err) throw err;
console.log("Data:", data.trim());
});
console.log("End non-blocking read");
// Output:
// Start non-blocking read
// End non-blocking read
// Data: <hostname>
Measuring Event Loop Lag
Use the blocking event loop lag to detect performance issues. Node.js provides Process.hrtime or perf_hooks for measurement.
const { performance, PerformanceObserver } = require("node:perf_hooks");
const obs = new PerformanceObserver((items) => {
items.getEntries().forEach((entry) => {
console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
});
});
obs.observe({ type: "measure" });
performance.mark("start-blocking");
const data = require("node:fs").readFileSync(__filename, "utf8");
performance.mark("end-blocking");
performance.measure("Blocking File Read", "start-blocking", "end-blocking");
Converting Blocking to Non-Blocking Patterns
Replace sync methods with async alternatives. For CPU-heavy operations, use worker threads or split work across chunks.
const fs = require("node:fs");
const { promisify } = require("node:util");
const readFileAsync = promisify(fs.readFile);
async function processFiles(files) {
const results = await Promise.all(
files.map((file) => readFileAsync(file, "utf8").catch(() => null))
);
return results.filter(Boolean);
}
processFiles(["file1.txt", "file2.txt"]).then(console.log);
Common Mistakes
1. Using JSON.parse on Large Payloads Synchronously
JSON.parse blocks the event loop. For large payloads, stream the data and parse incrementally or use worker threads.
2. Blocking with Every Synchronous Method
Many developers assume fs.readFileSync is fine for small files. Multiple concurrent small reads add up to significant blocking.
3. Not Using Streams for Large Data
Loading entire files into memory with readFileSync or readFile blocks both event loop and memory. Use streams for large files.
4. CPU-Intensive Loops on the Main Thread
Array operations, cryptography, and data transformation on large datasets block the event loop. Use worker threads.
5. Assuming async-await Is Always Non-Blocking
Promise.all does not make blocking operations non-blocking. Only the event loop can be blocked by synchronous code inside async functions.
Practice Questions
1. What happens to the event loop during a blocking call?
The event loop stops processing entirely. No timers, I/O, or other callbacks execute until the blocking call completes.
2. How do you convert readFileSync to non-blocking?
Use fs.readFile with a callback or fs.promises.readFile with async-await.
3. What is the best way to handle CPU-bound tasks in Node.js?
Use worker threads (Worker) to offload CPU work without blocking the event loop.
4. How can you measure event loop lag?
Use process.hrtime.bigint() or performance.now() before and after known sync operations, or use the perf_hooks module.
5. Challenge: Write a benchmark that shows blocking vs non-blocking latency with concurrent operations.
const http = require("node:http");
let blocking = false;
const server = http.createServer((req, res) => {
if (blocking) {
require("node:fs").readFileSync("/etc/hostname");
}
res.end("ok");
});
server.listen(3000);
FAQ
Mini Project: Event Loop Lag Monitor
Build a real-time event loop lag monitoring utility.
const { performance } = require("node:perf_hooks");
function monitorLag(intervalMs = 1000) {
function check() {
const before = performance.now();
setImmediate(() => {
const lag = performance.now() - before;
if (lag > 50) {
console.warn(`Event loop lag detected: ${lag.toFixed(2)}ms`);
}
setTimeout(check, intervalMs);
});
}
check();
}
monitorLag(500);
What's Next
Node.js Async Patterns Node.js Error Handling Node.js Streams
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro