You know that Node.js is based on an event-driven non-blocking I/O model. You may be wondering what does it mean for something to be blocking or something not to be blocking. In fact, there are both types of operations available in Node.js. However, in order to create a performant applications, only one kind should be used.
Accessing external resources
Blocking or non-blocking operating model is relevant when dealing with input/output operations. Writing to a resource or reading from a resource are considered I/O operations. The targets for such operations can be anything from files to network sockets to screen to keyboard. Any resource that is not accessible instantly at the machine instruction level but involves waiting instead requires I/O communication.
Computer memory, on the other hand, can be addressed instantly and does not require waiting. Accessing memory is therefore not considered an I/O operation. In fact, the random-access memory (RAM) name originates from its ability to access any location in the same amount of time irrespective of its physical location.
Blocking term originates from the operating system process model. A multitasking operating system labels each process with a state depending on how ready they are to be put on the CPU for execution. A process is labeled blocked if it is not ready for execution but is instead waiting for an I/O event to take place. I/O events indicate progress or completion in an I/O operation, for example "resource available" or "write complete".
Performing a blocking system call causes the process to enter the blocked state. Control is let back to the process only after the I/O event that is being waited upon occurs.
In Node.js the filesystem module contains fs.xSync methods that provide blocking versions of the filesystem operations.
Using blocking version, the call will not return until the I/O is completed. When the call returns, it provides the results for the operation as return value. The results are returned synchronously.
A blocking call causes results to be returned synchronously.
A non-blocking operation does not wait for I/O to complete. When blocking operation caused the process to be put on the back burner at the operating system level, performing a non-blocking I/O operation the process continues to run instead. A non-blocking call initiates the operation, leaves it for the operating system to complete returning immediately without any results. Alternate means are then used to determine completion of the I/O operation.
A performant application uses non-blocking I/O operations. This allows a single process to serve multiple requests at the same time. Instead of process being blocked and waiting for single I/O operation to complete, this waiting time is used to serve other requests. All Node.js libraries and the Node core api offer non-blocking operations with few minor exceptions.
The filesystem example written using a non-blocking call.
In this version, the readFile call returns immediately without any results. The current code block returns and only after the I/O operations are complete will the callback be called. It is said that the results are returned asynchronously.
A non-blocking call causes results to be returned asynchronously.
Blocking vs non-blocking
Blocking call waits for the I/O operation to complete before returning. Its results are returned synchronously. Nothing else in that process takes place during the waiting. In contrast, non-blocking call returns immediately without results and uses alternate means to check for completion. Other processing can be done while waiting and the results are returned asynchronously. Node.js libraries and core api provide non-blocking calls that can be used to build performant applications. Such applications make use of I/O waiting time to serve other requests.
Node.js doesn't wait for your database call to finish?
Learn the right mental model for Node apps. Get free email course and two chapters from Finish Your Node App book.