Skip to main content

Command Palette

Search for a command to run...

Blocking vs Non-Blocking Code in Node.js

Published
3 min read
Blocking vs Non-Blocking Code in Node.js
S

Learning web development in public. Writing simple, real-world explanations about web development concepts. Helping beginners understand why things work, not just how.

When working with Node.js, one of the most important concepts to understand is the difference between blocking and non-blocking code. This directly affects how fast and scalable your application will be.

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


1. What Blocking Code Means

Blocking code is when a task stops the execution of the program until it is finished.

In simple terms:

  • The system waits

  • Nothing else can run during that time

Example:

const fs = require('fs');

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

console.log('This runs after file is read');

What happens:

  • File is read completely first

  • Only then the next line runs

This blocks the main thread.


2. What Non-Blocking Code Means

Non-blocking code allows the program to continue executing while a task is running in the background.

Instead of waiting:

  • The task is started

  • The program moves forward

  • Result is handled later

Example:

const fs = require('fs');

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

console.log('This runs before file is read');

What happens:

  • File reading starts

  • Program continues immediately

  • Result is printed later


3. Why Blocking Slows Servers

In Node.js, everything runs on a single thread.

If one request uses blocking code:

  • It stops the thread

  • Other requests must wait

Example scenario:

  • User A → file read (blocking)

  • User B → simple request

User B will have to wait until User A’s task is finished.

This leads to:

  • Slow response times

  • Poor scalability

  • Bad user experience


4. Async Operations in Node.js

Node.js is designed to use asynchronous (async) operations.

Instead of blocking:

  • Tasks are handled in the background

  • Callbacks, Promises, or async/await are used

Example with async/await:

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

async function readFile() {
  const data = await fs.readFile('file.txt', 'utf-8');
  console.log(data);
}

readFile();
console.log('This runs first');

Even though await looks like it pauses execution, Node.js is still handling it efficiently without blocking other operations.


5. Real-World Examples


a) File Reading

Blocking:

fs.readFileSync('data.txt');

Non-blocking:

fs.readFile('data.txt', callback);

b) Database Calls

Blocking (bad practice):

  • Waiting for database response before doing anything else

Non-blocking (correct approach):

db.query('SELECT * FROM users', (err, result) => {
  console.log(result);
});

Or using async/await:

const users = await db.query('SELECT * FROM users');

c) API Calls

Non-blocking example:

fetch('/api/data')
  .then(res => res.json())
  .then(data => console.log(data));

console.log('Runs immediately');

Final Understanding

  • Blocking code stops execution until a task finishes

  • Non-blocking code allows other tasks to run in parallel

  • Blocking operations slow down Node.js servers

  • Node.js is built around asynchronous, non-blocking behaviour

  • Real-world applications rely heavily on non-blocking code


Summary

Understanding blocking vs non-blocking code is essential for writing efficient Node.js applications. Since Node.js uses a single-threaded model, blocking operations can freeze the entire server and delay all incoming requests. Non-blocking code solves this by allowing tasks to run in the background while the system continues processing other requests. By using asynchronous patterns like callbacks, promises, and async/await, you can build fast, responsive, and scalable applications that handle multiple users efficiently.