Blocking and Non-Blocking Node.js operations
Blocking Operations
A blocking operation occurs when the execution of javascript operations is pulsed and must wait for a non-javascript operation to complete executing. By non-javascript operations, I mean operations like reading or writing data to a file, making a network request, reading or persisting data to a database e.t.c.
Blocking operations occurs in node.js because Javascript in it's most basic form is a synchronous, blocking, single-threaded language which can only perform several operations one at a time and since Node.js is a cross-platform JavaScript runtime environment, it runs its operations in a single process without creating a new thread for every request.
Let's look at the code snippet below;
01: const fs = require('fs');
02:
03: //reads a file in a synchronous and blocking way
04: const readFileSynchronously = (filepath) => {
05: console.log('***Reading file***');
06: const data = fs.readFileSync(filepath, {encoding: 'utf8'});
07: console.log(data);
08: console.log('***Data read***');
09: return data;
10: }
11:
12: const calculateArraySum = (arr) => {
13: return arr.reduce((acc, curr) => acc + curr )
14: }
15:
16: readFileSynchronously('./text.txt');
17:
18: const sum = calculateArraySum([1, 2, 3, 4, 5, 6, 7]);
19: console.log('Sum: ', sum);
20:
The text file contains the following text
This is a text file.
You can write anything here.
Running the above code displays the following on the terminal
***Reading file***
This is a text file.
You can write anything here.
***Data read***
Sum: 28
Let's understand what's happening here. Line 4 creates a function readFileSynchronously. The fs (file system) method readFileSync on line 6 performs a synchronous file reading and blocks all other operation till it finish reading the file.
Looking at the code output, line 16 calls the readFileSynchronously function which prints line 5 then waits for the data to be read, print it on line 7 then prints line 8. In all these, line 18 waits for the function call to complete before executing and line 19 prints the sum of the array.
The code looks simple and straight forward but it has the disadvantage of the third line blocking the execution of the additional javascript code. Imagine if we have a super large file that can take several seconds to read, then the execution of the additional javascript code will have to wait for the data to finish reading. Meanwhile, the calculateArraySum function need not bother with the data being read from the file and so there is no point waiting for the data to be read before executing.
Non-blocking operations
Node.js provides a set of asynchronous I/O primitives in its standard libray that prevent Javascript code from blocking other additional javascript code. In general, Node.js libraries are written using an asynchronous, non-blocking paradigms.
Let's rewrite the blocking operation to work asynchronously
01: const fs = require('fs');
02:
03: const readFileAsynchronously = (filepath) => {
04: console.log('***Reading file***');
05: fs.readFile(filepath, {encoding: 'utf8'}, (err, data) => {
06: console.log(data);
07: console.log('***Data read***');
08: });
09: }
10:
11: const calculateArraySum = (arr) => {
12: return arr.reduce((acc, curr) => acc + curr )
13: }
14:
15: readFileAsynchronously('./text.txt');
16:
17: const sum = calculateArraySum([1, 2, 3, 4, 5, 6, 7]);
18: console.log('Sum: ', sum);
Running the above in node.js environment will give the output below
***Reading file***
Sum: 28
This is a text file.
You can write anything here.
***Data read***
Take a look at the output, line 18 executes and prints the sum before the data from the file is displayed on line 6. This happened because the fs method readFile performs an asynchrounous operation and does not block the execution of other javascript code but instead it acceptes a function (popularly referred to as a callback function) as it third argument and calls that function with the data when it's done reading the text file or calls it with an error if it encounters an error.
In summary, node.js runs in a single thread, but it provides a set of different I/O primitives in its library that runs asynchronously, thereby providing code that runs concurrently.
Thank you for your good and understandable article