Skip to main content

Command Palette

Search for a command to run...

Blocking vs Non-Blocking Code

Lets understand block in terms of Real life examples

Published
β€’4 min read
Blocking vs Non-Blocking Code
S

Frontend Developer πŸ’» | Fueled by curiosity and Tea β˜• | Always learning and exploring new technologies.

Blocking

Who better know than us, what blocking means. Sad story...
Whether it’s your ex-girlfriend or your Node.js server, being blocked fundamentally ruins everyone's day.

Symptoms of Blocking

  • Timer Freeze.

  • Callbacks waiting..

Non Blocking

Opposite of blocking. It's smooth process.

Example

Blocking (synchronous)

const fs = require('fs');

console.log('Before read');

const data = fs.readFileSync('./data.txt', 'utf8'); // ← thread stops here
console.log(data);

console.log('After read'); 
Before read
[file contents]
After read

The entire thread pauses at readFileSync. If this file is on a slow disk, or it's a large file, your server is frozen for that entire duration. Every other user waiting on your server waits too.

Non-blocking (asynchronous) file read:

const fs = require('fs');

console.log('Before read');

fs.readFile('./data.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data); // ← runs when the file is done reading
});

console.log('After read'); // ← runs IMMEDIATELY, doesn't wait
Before read
After read
[file contents]

This confuses developers the first time they see it. "After read" prints before the file contents? Yes. Because the file read is happening in the background. The callback fires later, when the OS has finished reading. The thread kept running after registering the callback.

This reordering of output is the most reliable indicator that code is truly non-blocking.

Why Blocking Slows Servers:

Let's say your server handles a request that reads a 10ms file. Ten milliseconds sounds nothing. But under load, it compounds fast.

With blocking code:

User A request arrives  β†’ file read starts β†’ [10ms frozen] β†’ response sent
User B request arrives  β†’ queued, waiting while A is frozen
User C request arrives  β†’ queued, waiting while A is frozen
...

If 100 users hit this endpoint simultaneously and each file read takes 10ms, the last user waits 1000ms β€” 1 full second β€” not because their file read took that long, but because they're waiting for 99 people ahead of them to get their turn on a frozen thread.

With non-blocking code:

User A request arrives β†’ readFile registered β†’ callback queued β†’ thread free
User B request arrives β†’ readFile registered β†’ callback queued β†’ thread free
User C request arrives β†’ readFile registered β†’ callback queued β†’ thread free
...
All file reads happen in parallel at OS level
All callbacks fire roughly at t=10ms
All responses sent near-simultaneously

Same 100 users. Total wait time: ~10ms for everyone. Not 1000ms. The non-blocking version is 100x faster in this scenario β€” not because the disk ran faster, but because the thread stopped waiting.

What "Async" Means in Practice

You'll see "async" used interchangeably with "non-blocking" in the Node.js world. They're not quite identical, but in practice the distinction rarely matters for application code.

Node.js gives you three ways to write non-blocking code, and they've evolved over time:

Callbacks (original Node.js style)

fs.readFile('./file.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});

Promises

const fs = require('fs').promises;

fs.readFile('./file.txt', 'utf8')
  .then(data => console.log(data))
  .catch(err => console.error(err));

Async/Await (modern Node.js)

const fs = require('fs').promises;

async function readMyFile() {
  try {
    const data = await fs.readFile('./file.txt', 'utf8');
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

readMyFile();

Summary

Blocking code freezes your entire server. Non-blocking code keeps your server responsive even under load. This isn't just good practice in Node.js it's the foundational contract that makes the single-threaded model work.

The Sync functions exist. Use them only when you're not handling concurrent requests (scripts, startup, CLI tools). In a running server, they're almost always a bug waiting to manifest under load.

Async/await is the cleanest way to write non-blocking code today. Under the hood it's still callbacks and promises but the syntax keeps your code readable while the event loop stays unblocked.

Write non-blocking code, and Node.js rewards you with performance that surprises people who've only ever used thread-per-request servers.

I’m currently deep-diving into the JavaScript, building projects and exploring the internals of the web. If you're on a similar journey or just love talking about JavaScript, let’s stay in touch!

Keep coding and keep building.

Node JS

Part 3 of 6

Sharing my Node JS journey. What i learn and have experienced.

Up next

Is Node.js Really Single-Threaded?

How Node.js Handles Multiple Requests with a Single Thread