Skip to main content

Blocking and non-blocking code

What blocking and non-blocking code is

TermWhat it means
Blocking codeProgram execution stops until the operation finishes
Non-blocking codeThe program keeps running, without waiting for the operation to finish

1. Blocking code

The program waits for an operation (for example, reading a file or querying a database) to finish before moving on.

Example (synchronous file read in Node.js):

javascript
const fs = require('fs'); console.log('1. Start'); const data = fs.readFileSync('file.txt', 'utf8'); // a blocking call console.log('2. File read'); console.log('3. End');

Output:

javascript
1. Start 2. File read 3. End

While fs.readFileSync reads the file, the whole thread is blocked, Node.js can't handle any other requests or events.

2. Non-blocking code

The program doesn't wait, it starts the operation and immediately moves on. Once the operation finishes, the result comes back through a callback, Promise, or async/await.

Example (asynchronous file read):

javascript
const fs = require('fs'); console.log('1. Start'); fs.readFile('file.txt', 'utf8', (err, data) => { console.log('2. File read'); }); console.log('3. End');

Output:

javascript
1. Start 3. End 2. File read

Here fs.readFile runs in the background (through libuv), while the main thread isn't blocked and keeps working. Once the file is ready, the callback lands in the event queue (the event loop).

3. What makes code blocking

Any operation that waits for I/O to finish on the main thread is blocking:

  • fs.readFileSync(), fs.writeFileSync()
  • JSON.parse() on huge data
  • heavy computation (for example, a loop over millions of iterations)
  • synchronous network requests (if Node.js had them)

The problem:

In single-threaded Node.js, one blocking section of code can freeze the entire server.

4. How Node.js makes code non-blocking

Node.js implements a non-blocking I/O model using:

  • the event loop,
  • libuv (the library that creates the background thread pool).

The principle:

  1. JS code starts an operation (for example, reading a file).
  2. It's handed to libuv → it runs on another thread.
  3. The main thread keeps running.
  4. Once the operation finishes → the callback/Promise goes back to the event loop.

An analogy

Picture a coffee shop:

ScenarioWhat happens
BlockingThe barista takes an order, waits for the coffee to brew, and only then takes the next order.
Non-blockingThe barista takes an order, hands it off to be made, and immediately takes the next one. Once the coffee is ready, they just hand it over.

Result: more customers served, nobody waits for nothing.

5. A practical example: a web server

A blocking server

javascript
const http = require('http'); const fs = require('fs'); http.createServer((req, res) => { const data = fs.readFileSync('data.json'); // blocks the thread! res.end(data); }).listen(3000);

One request "hangs", everyone else waits.

A non-blocking server

javascript
const http = require('http'); const fs = require('fs'); http.createServer((req, res) => { fs.readFile('data.json', (err, data) => { res.end(data); }); }).listen(3000);

The server handles many requests at once, because file reads run in the background.

6. Comparison table

CriterionBlocking codeNon-blocking code
Execution modelSynchronousAsynchronous
Execution threadOne, and it waitsOne, but it doesn't wait
PerformanceSlow under I/OHigh under I/O
Good forSimple scriptsServers, APIs
Example in Node.jsfs.readFileSync()fs.readFile()

Summary

  • Blocking code halts the program until the operation finishes.
  • Non-blocking code lets other tasks run while the operation runs in the background.
  • Node.js is built around non-blocking I/O and the event loop, which is why it can handle thousands of requests on a single thread.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.