This distinction is the single most important idea in Node.js, and everything else in this chapter builds on it.
Blocking, in plain terms
Blocking code makes the program stop and wait. Nothing else can happen until that one operation finishes.
const fs = require('fs');
console.log('Reading file...');
const data = fs.readFileSync('report.txt', 'utf8'); // execution halts here
console.log('File contents loaded:', data.length, 'characters');
console.log('Continuing on...');
Each line waits for the one before it. If report.txt were a 2GB file, the whole program would simply freeze until the read finished — no other code runs in the meantime.
Non-blocking, in plain terms
Non-blocking code starts a task and moves on immediately, coming back to handle the result later.
const fs = require('fs');
console.log('Reading file...');
fs.readFile('report.txt', 'utf8', (err, data) => {
if (err) return console.error(err);
console.log('File contents loaded:', data.length, 'characters');
});
console.log('Continuing on...');
Run this and 'Continuing on...' prints before the file contents log — because the program didn't wait around. The callback runs later, whenever the file read actually finishes in the background.
Seeing the difference in a server
A blocking server handles one visitor completely before it can even look at the next one:
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
const data = fs.readFileSync('largeReport.txt'); // blocks every other request too
res.end(data);
}).listen(3000);
If that file takes two seconds to read, every single visitor during those two seconds is stuck waiting, even ones asking for something completely different. Swap in the non-blocking version and the server stays responsive to everyone:
http.createServer((req, res) => {
fs.readFile('largeReport.txt', (err, data) => {
if (err) { res.writeHead(500); return res.end('Error'); }
res.end(data);
});
}).listen(3000);
When blocking code is actually fine
Synchronous methods aren't evil — they're simpler to read and reason about, and totally acceptable for quick, one-time setup work that happens before your server starts handling traffic (reading a config file at startup, for instance). The rule of thumb: avoid blocking, synchronous calls inside anything that runs while your server is actively serving requests.