Blocking and Non-Blocking Code

Let’s say 3 couple came to a restaurant and sitting on separate dining tables. Waiter goes to Couple #1 and takes down the order. Waiter then goes to the kitchen and passes on your order to chef. Say, while that chef is preparing the food, the Waiter waits for it to complete. Once food is prepared, he delivers the food to Couple #1 and then proceeds to take the order of Couple #2. Imagine that he follows these same steps to fulfill the orders of Couple #2 and #3. In Node, we call this kind of process as Blocking, as Waiter is stopping by Chef and wasting the time of other customers.

Here, Waiter takes 60 minutes to serve 3 customer orders

If the Waiter returns after passing on the order to Chef to take down orders of other customers, and only goes back to Chef once the food is ready, he will be able to serve multiple requests in less time. In Node, we call this kind of process as non-blocking.

Here, Waiter takes only 30 mins to fulfill those orders.

We can relate this example in Node.js with blocking operation, synchronous call and asynchronous call. Cooking is a blocking operation. If waiter waits for the cook to be prepared before taking other orders, we call it a Synchronous flow. If waiter moves on to the next order after instructing chef is an Asynchronous flow. Once food is prepared Chef will call upon the waiter serve the food, in Node.js world it is a callback.

If code waits for an operation to complete, that operation is a blocking operation and the code is synchronous code. If the code doesn’t wait for the operation to complete, we say that it executed the operation asynchronously.

But we wanted to perform some action after the blocking operation completes. So we pass on a function to the blocking operation instructing to call that function after completion of the blocking operation. This function is called as a callback function. Once, the blocking operation is completes execution, it will call the callback function.

console.log('Take Order');
console.log('Pass-on Order to Chef');
setTimeout(function(){
    console.log('Food is prepared, please comeback and serve to customer');
}, 3000);
console.log('Take other orders');

Here, flow doesn’t stop at setTimeout function, instead “Take other orders” will be printed right after “Pass-on Order to Chef”. The function within setTimeout is called callback function. This callback function will be called after 3000 milliseconds elapse, printing the “Food is prepared” statement.

Performing IO and network operations like calling a web service are time taking activities. Since Node.js is single threaded, it needs to ensure that these activities are not blocking the thread. Understanding this issue, Node.js provides asynchronous APIs to perform network and file system operations. Functions in these modules are non-blocking. Similarly, it provides synchronous counter parts, if there is a need arise.

Asynchronously reading a file using non-blocking method:

var fs = require('fs');
fs.readFile('test-file.txt','utf8', function(error, contents) {
    console.log(contents);
});
console.log('It is not end of the program.');

Module ‘fs’ contains methods to perform file system operations. Here, readFile is a non-blocking method.

Synchronously reading a file using blocking method:

var fs = require('fs');
console.log('Reading the file...');
var contents = fs.readFileSync('test-file.txt','utf8');
console.log(contents);
console.log('End of the program.');

In this example “End of the program.” will be printed after reading the file, as we are using readFileSync method which blocks the thread.