Back to results for “Performance Optimization & Profiling”
Node.js › Event Loop & Non-blocking IO

What does 'non-blocking I/O' mean, and how does a Node.js function like fs.readFile() achieve it compared to fs.readFileSync()?

Intermediate

Answer

Non-blocking I/O means initiating an I/O operation (reading a file, querying a network resource) and immediately continuing to execute other code rather than pausing to wait for that operation to complete -- fs.readFile() delegates the actual file-reading work to libuv's thread pool (or the OS's async I/O facilities) and returns control to the event loop immediately, invoking a callback only once the data is ready, whereas fs.readFileSync() blocks the entire single JavaScript thread until the file read completes, preventing any other code (including handling other incoming requests) from running during that time.
// Blocking: freezes the entire event loop until the file is fully read
const data = fs.readFileSync('large-file.txt');

// Non-blocking: the event loop remains free to handle other work meanwhile
fs.readFile('large-file.txt', (err, data) => {
  console.log('File read complete');
});
Real-world example A web server that accidentally used fs.readFileSync() inside a request handler to serve a large file became unresponsive to all other concurrent users during that single read; switching to the non-blocking fs.readFile() (or better, a readable stream) resolved the issue by no longer freezing the entire server for every request.

Common follow-ups: In what specific, narrow scenario is a synchronous function like readFileSync() actually the correct choice?;How does non-blocking I/O in Node.js compare conceptually to how a traditional multi-threaded server achieves concurrency?