Non-Blocking I/O: Don't Block the Event Loop
Non-blocking I/O lets your program do other work while waiting for slow operations like network requests. It's the core of Node.js, allowing a single thread to serve many users.
WHY IT EXISTS To solve the problem of idle resources. In a traditional blocking model, a program thread waiting for a database response or a file to be read is stuck doing nothing. Non-blocking I/O allows that single process to handle other tasks in the meantime, dramatically improving efficiency and throughput for I/O-bound applications.
THE MENTAL MODEL Think of a restaurant with a single, very fast waiter (the Node.js process). With blocking I/O, the waiter takes an order, walks to the kitchen, and waits there until the dish is fully cooked before taking another order. The entire restaurant's service grinds to a halt. With non-blocking I/O, the waiter takes an order, gives it to the kitchen (the underlying system), and immediately moves to another table. The kitchen notifies the waiter when a dish is ready. This way, the single waiter can serve many tables concurrently.
HOW IT WORKS When you initiate a non-blocking operation, like reading a file with fs.readFile, Node.js doesn't wait. It hands the request off to a low-level system library (libuv), which performs the operation outside the main JavaScript execution thread, often using a thread pool. Node.js immediately continues executing your code. Once the I/O operation is complete, a callback function is placed on the event queue. When the main call stack is empty, the event loop picks up the callback and executes it with the result.
WHEN TO USE IT Use non-blocking I/O for any operation that involves waiting for external resources like the network or filesystem. This is the default and idiomatic way to write Node.js code. It is essential for building scalable web servers, APIs, or any application that handles multiple concurrent requests. All standard library I/O functions in Node.js have asynchronous, non-blocking versions that accept a callback or return a Promise.
WHEN NOT TO USE IT Blocking I/O can be acceptable for simple, one-off scripts where concurrency isn't a concern. For example, a script that reads a single configuration file on startup might use the blocking fs.readFileSync for simplicity, as the process isn't handling user requests yet. However, you must never use blocking calls within a server request handler or any other concurrent context, as it will freeze the entire event loop.
ONE CANONICAL EXAMPLE A web server needs to read a user's profile from a database. A blocking approach would be const user = db.querySync('SELECT * FROM users');. During this query, the entire server is frozen and cannot handle any other incoming requests. The non-blocking approach is db.query('SELECT * FROM users', (err, user) => { /* process user */ });. While the database is working, the server is free to handle hundreds of other requests.
Read the original → nodejs.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.