Synchronous vs. Asynchronous in Node.js every Programmer should know about.

Synchronous Code vs. Asynchronous Code in Node.js.

·

4 min read

Synchronous vs. Asynchronous in Node.js every Programmer should know about.

Usually Synchronous and Asynchronous code are used for handling concurrency in JavaScript. As there are multiple ways of handling concurrency on programming languages. we are going to explore synchronous and Asynchronous Calls. Meanwhile they could also be defined as Blocking vs Non-Blocking.

Node.js, being an asynchronous platform, doesn't wait around like for file I/O to finish - Node.js uses callbacks. A callback is a function called at the completion of any particular given task; this prevents any blocking, and allows rest of the code to be run in the meantime.

nodeblock.png

Lets start with some examples where we will be using both Synchronous and Asynchronous code to run same logic.

In Synchronous program :

function process_Data () {
  var data = fetchData ();
  data += 1;
  return data;
}

The above code works fine. However, if fetchData () takes a long time to load the data (maybe it is streaming it off the drive or could be the internet), then this causes the whole program to 'block' - otherwise known as idle or waiting - until it loads the data.

But Node.js, being an asynchronous/Non-Blocking platform, doesn't wait around for particular function like file I/O to finish - Node.js uses callbacks ie, Callback is a function called at the end once the task is completed, this prevents any blocking, and allows other code to be run in the meantime.

In Asynchronous program

function process_Data (callback) {
  fetchData(function (err, data) {
    if (err) {
      console.log("An error has occurred !!!");
      return callback(err);
    }
    data += 1;
    callback(data);
  });
}

At first it might look unnecessarily complicated, but callbacks are the foundation of Node.js. Callbacks allows you to have as many IO operations as your OS can handle happening at the same time. As when working with the web servers multiple blocking queries and pending requests, so while executing blocking queries asynchronously gives you liberty to continue working without waiting for the blocking operations callback, this method saves more time. This is the major improvement.

Lets compare two different ways of reading files using a Synchronous I/O model and then using a non-Asynchronous I/O model.

In Synchronous program :

const fs = require('fs');
console.log("start");
const data = fs.readFileSync('/file1
![nodeblock.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1620393730784/MZ961Cgdq.png)
md'); // blocks here until file is read
console.log("end");
endfunction(); // will run after console.log

In Asynchronous program

const fs = require('fs');
console.log("start");
fs.readFile('/file1.md', (err, data) => {
  if (err) throw err;
console.log("end");
});
endfunction(); // will run before console.log

The synchronous program looks simpler then the Asynchronous program but has a disadvantage as the second line is blocking the execution as it will wait until the task ends. Note that in the synchronous version if an error is thrown it will need to be caught or the process will crash and have to execute again. In the asynchronous version, it is up to the author to decide whether an error should throw as shown.

In the synchronous program above, console.log will be called before endfunction(). In the second example fs.readFile() is non-blocking so JavaScript execution can continue and endfunction() will be called first. The ability to run endfunction() without waiting for the file read to complete is an effective design choice that allows for higher throughput.

What happens when mixing Synchronous and Asynchronous code.

There are some patterns which are more likely to avoid while writing any program lets try this with an example:

const fs = require('fs');
fs.readFile('/file1.md', (err, data) => {
  if (err) throw err;
  console.log(start);
});
fs.unlinkSync('/file1.md'); // unlinkSync will delete the file

In the above example, fs.unlinkSync() is likely to be run before fs.readFile(), which would delete file1.md before it is actually read. A better way to write this, which is completely non-blocking and can execute in the correct order is:

const fs = require('fs');
fs.readFile('/file1.md', (readFileErr, data) => {
  if (readFileErr) {
      throw readFileErr;
}
  console.log(start);
  fs.unlink('/file1.md', (unlinkErr) => {
    if (unlinkErr) throw unlinkErr;
  });
});

The above program places a non-blocking call to fs.unlink() within the callback of fs.readFile() which allows the correct order of operations.

In JavaScript, we can handle asynchronous code using:

Callbacks
Promises
Async/Await functions
Generators .

Conclusion:

Now as we hopefully got convinced why writing asynchronous (non-blocking)code is necessary. So far, we used callbacks which handles all functions in parallel manner helps user to save time and avoid unnecessary crash.