Skip to main content

Command Palette

Search for a command to run...

Blocking vs Non-Blocking Code in Node.js

Updated
2 min readView as Markdown

What is Blocking Code?

Blocking code is code that stops the execution of further operations until the current task finishes.

In simple words:

“Wait here until this work is complete.”

During this time, Node.js cannot continue executing the next line or handle other incoming requests.

Example of Blocking Code

const fs = require("fs");

console.log("Start");

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

console.log(data);

console.log("End");

Output

Start
(file content)
End

What Happens Internally?

The readFileSync() method is synchronous.

Node.js:

  1. Starts reading the file

  2. Waits until the file is fully read

  3. Then moves to the next line

Until the file operation finishes, the entire thread is blocked.

Why Blocking Code is Bad for Servers

Node.js uses a single-threaded event loop to handle requests.

If one request runs blocking code:

  • Other requests must wait

  • Server response time increases

  • Performance drops under heavy traffic

Imagine:

  • User A uploads a huge file

  • Server uses blocking file read

  • User B and User C must wait

This creates a bottleneck.

What is Non-Blocking Code?

Non-blocking code allows Node.js to start a task and continue executing other operations without waiting.

Instead of pausing execution, Node.js delegates the operation to the system and continues handling other work.

When the task finishes, a callback, promise, or async function handles the result.

Example of Non-Blocking Code

const fs = require("fs");

console.log("Start");

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

    console.log(data);
});

console.log("End");

Output

Start
End
(file content)

How Node.js Handles Async Operations

Node.js uses:

  • Event Loop

  • Callback Queue

  • Worker Threads (via libuv)

When an async task starts:

  • Node delegates heavy operations like:

    • File system access

    • Database queries

    • Network requests

  • The main thread remains free

  • Once completed, the result returns to the event loop

This architecture makes Node.js highly scalable.

Real-World Example: File Reading

Blocking File Read

const fs = require("fs");

const data = fs.readFileSync("largeFile.txt", "utf-8");

console.log(data);

Problem:

  • Server waits until the file is fully read

  • Other users may experience delays

Non-Blocking File Read

const fs = require("fs");

fs.readFile("largeFile.txt", "utf-8", (err, data) => {
    if (err) throw err;

    console.log(data);
});

Benefit:

  • Server continues handling requests

  • Better responsiveness