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:
const data = fs.readFileSync('file.txt'); // blocks execution
console.log('File read'); // runs only after the file is readMeanwhile 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.
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
console.log('File read');
});
console.log('The code is not blocked!');What happens:
- Node.js starts reading the file.
- The operation moves to a background thread (via libuv).
- The program does not wait and runs the next code.
- 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
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
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 type | How it works | Problem / advantage |
|---|---|---|
| Blocking | Runs operations sequentially | Sits idle while waiting |
| Non-blocking | Runs operations asynchronously, through the event loop | High 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 readyA concise answer to help you respond confidently on this topic during an interview.