Skip to main content

Command Palette

Search for a command to run...

Blocking vs Non-Blocking Code in Node.js

Updated
4 min readView as Markdown

When building backend systems, one of the most important concepts to understand is how your server handles tasks. In Node.js, this comes down to blocking vs non-blocking code — a core reason why Node.js is fast and scalable.

Let’s break it down in a simple, practical way.


🚧 What is Blocking Code?

Blocking code means: 👉 The program waits for a task to finish before moving to the next line.

Example (Synchronous / Blocking)

const fs = require("fs");

const data = fs.readFileSync("file.txt", "utf-8");
console.log(data);

console.log("Next line");

What happens here:

  1. Node starts reading the file

  2. It pauses everything else

  3. Only after reading completes → it prints "Next line"

❗ Problem

If the file is large or slow to access:

  • The entire server is stuck

  • No other user request can be processed


⚡ What is Non-Blocking Code?

Non-blocking code means: 👉 The program does not wait — it moves forward and handles the result later.

Example (Asynchronous / Non-Blocking)

const fs = require("fs");

fs.readFile("file.txt", "utf-8", (err, data) => {
  console.log(data);
});

console.log("Next line");

What happens here:

  1. File read starts in background

  2. Node immediately moves on

  3. "Next line" prints first

  4. File result prints later


🧠 Simple Analogy

Think of it like ordering food:

Blocking

  • You go to a restaurant

  • Stand at the counter until your food is ready

  • Do nothing else

Non-Blocking

  • You order food

  • Sit with friends / do other work

  • Get notified when food is ready

👉 Node.js works like the second scenario.


🐢 Why Blocking Code Slows Down Servers

Node.js uses a single-threaded event loop.

That means:

  • Only one main thread handles all requests

  • If one request blocks → everything else waits

Real Impact:

If 100 users hit your server:

  • Blocking → requests handled one-by-one ❌

  • Non-blocking → requests handled concurrently ✅


🔄 Async Operations in Node.js

Node.js is designed around asynchronous (async) operations, such as:

  • File system operations (fs)

  • Database queries

  • API calls

  • Network requests

These tasks are:

  • Delegated to background workers

  • Returned via callbacks, promises, or async/await


📂 Real-World Example: File Handling

Blocking Version

app.get("/", (req, res) => {
  const data = fs.readFileSync("largeFile.txt", "utf-8");
  res.send(data);
});

🔴 Problem:

  • Each request waits for file read

  • Server becomes slow under load


Non-Blocking Version

app.get("/", (req, res) => {
  fs.readFile("largeFile.txt", "utf-8", (err, data) => {
    res.send(data);
  });
});

🟢 Advantage:

  • Server continues handling other requests

  • Much better performance


🗄️ Real-World Example: Database Calls

Blocking (Bad Practice)

const user = db.getUserSync(id);

Non-Blocking (Correct)

db.getUser(id).then(user => {
  console.log(user);
});

or

const user = await db.getUser(id);

👉 Database operations are slow — always keep them async.


⚖️ Blocking vs Non-Blocking Summary

Feature Blocking Code ❌ Non-Blocking Code ✅
Execution Waits Continues immediately
Performance Slow Fast
Scalability Poor High
Use Case Rare (startup scripts) Almost everywhere in Node

🎯 When (If Ever) to Use Blocking?

Blocking code is acceptable when:

  • Running startup scripts

  • CLI tools

  • One-time operations (not handling users)

👉 Never use blocking code in production servers.


🚀 Final Takeaway

  • Node.js shines because of non-blocking architecture

  • Blocking code defeats its purpose

  • Always prefer:

    • Callbacks

    • Promises

    • async/await

👉 If your server feels slow, chances are you're accidentally blocking the event loop.