Skip to main content

What is non-blocking I/O?

What I/O is

I/O (Input/Output) covers every operation where a program interacts with the outside world, rather than just crunching numbers in memory:

  • reading and writing files (fs.readFile, fs.writeFile),
  • database queries,
  • HTTP requests,
  • network operations (TCP/UDP),
  • calling APIs, and so on.

These operations take time, because they depend on the disk, the network or the CPU.

Blocking I/O

In a blocking approach (for example, Python, PHP, Java by default):

  • While the program waits for one I/O operation to finish, it cannot do anything else.

Pseudocode example:

javascript
const data = fs.readFileSync('file.txt'); // blocks execution console.log('File read'); // runs only after the file is read

Meanwhile the CPU sits idle, waiting for the read to finish.

Non-blocking I/O

Node.js performs I/O asynchronously: it does not wait for the operation to finish and keeps running the rest of the code.

javascript
const fs = require('fs'); fs.readFile('file.txt', 'utf8', (err, data) => { console.log('File read'); }); console.log('The code is not blocked!');

What happens:

  1. Node.js starts reading the file.
  2. The operation moves to a background thread (via libuv).
  3. The program does not wait and runs the next code.
  4. When the read finishes, a callback / Promise / async/await fires, and the result comes back.

How it works under the hood

Node.js is built on the Event Loop mechanism and the libuv library:

  • libuv manages event queues and threads.
  • When an I/O operation finishes, an event about the result being ready lands in the event loop.
  • The callback (or then, await) runs once the data is available.

That is why even a single thread can handle thousands of connections at once.

Example: a blocking vs non-blocking server

Blocking example

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

Every request waits for the file read to finish. At 1000 requests, the queue "chokes".

Non-blocking example

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

Node.js starts reading the file and is immediately ready to accept other requests. The server stays responsive even under heavy load.

The main difference

I/O typeHow it worksProblem / advantage
BlockingRuns operations sequentiallySits idle while waiting
Non-blockingRuns operations asynchronously, through the event loopHigh throughput, scalability

Key advantages of non-blocking I/O in Node.js

  • Fast response times (no "freezing" during I/O)
  • One thread, thousands of requests
  • Resource savings (fewer threads, less memory)
  • A great fit for APIs, real-time apps and microservices

Short Answer

Interview ready
Premium

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