Asynchronous Javascript


JavaScript is a single-threaded language, which means that it can execute only one task at a time. But sometimes, we need to execute some code asynchronously, i.e. the code should not wait for the previous line to execute and should execute in parallel.

For example, you make an API call to fetch some data from the server, meanwhile, you will not want the whole program to wait for the response from the server. This is where asynchronous JavaScript comes into play.


Synchronous vs Asynchronous

Let's understand the difference between synchronous and asynchronous code with the help of an example.

Consider the following code snippet:

function asyncFunction() {
  console.log("Start");
  console.log("Middle");
  console.log("End");
}

asyncFunction();

When you run the above code, you will get the following output:

Start
Middle
End

As you can see, the code is executed synchronously, i.e. the code is executed line by line.

Now, let's make the above code asynchronous by using setTimeout() function.

function asyncFunction() {
  setTimeout(() => {
    console.log("Start");
  }, 2000);
  console.log("Middle");
  console.log("End");
}

asyncFunction();
Middle
End
Start

As you can see, the code is executed in an asynchronous manner, i.e. the code is not executed line by line.

Now what are all these stuff and how can it help you in your programming journey?

How asynchronous code can help you?

Suppose you make an IP address finding app, where you need to fetch the IP address data from the server. Now, if you make a synchronous API call, then the whole program will wait and your app will freeze until the data is fetched from the server.

That's where you need to use asynchronous code, so that the program will not wait for the response from the server and will continue to execute the code, as soon as the data is fetched from the server, it will be displayed on the screen.


How to make JavaScript code asynchronous?

There are two ways to make JavaScript code asynchronous:

  1. Callback Functions
  2. Promises

Let's understand both of them in detail.


1. Callback Functions

Callback functions are a way to make JavaScript code asynchronous. These functions are passed as an argument to another function to be executed later.

Here is a simple example of a callback function:

Example

function callbackFunction() {
  console.log("Callback function called");
}

function asyncFunction(callback) {
  setTimeout(() => {
    callback();
  }, 2000);
}

asyncFunction(callbackFunction);

Unable to connect how will you use this in your code to make it asynchronous? Let's understand this with the help of a real world example.

In the IP address finding application, when you make an API call to fetch the IP address data, then you will pass a callback function to the API call, which will be executed when the data is fetched from the server to display the data on the screen (or to do whatever you want to do with the data).

Example

// callback function to display the IP address data
function displayData(data) {
  alert(JSON.stringify(data));
  // do whatever you want to do with the data
}

// function to make API call
function getIPData(callback) {
  fetch("https://api.ipify.org/?format=json")
    .then(response => response.json())
    .then(data => callback(data));
}

// calling the function
getIPData(displayData);

// other code to be executed
console.log("Other code");

You can see how the code is executed asynchronously, i.e. the code is not executed line by line. The other code is not waiting for the response from the server and is executed immediately.

This is how you can make your JavaScript code asynchronous using callback functions.

Problem with callback functions

The biggest problem with callback functions is callback hell. When you have to make multiple asynchronous calls, then you have to nest the callback functions, which makes the code difficult to read and maintain.

We will talk about callback hell later in this tutorial.


2. Promises

Promises are another way to make JavaScript asynchronous. Promises are like a placeholder for future value. It is an object that may produce a single value sometime in the future.

First, we will see how Promise is used with asynchronous code and then we will learn more about promises in detail.

Tracking the previous example, let's see how we can use promises to make the code asynchronous.

Example

// function to make API call
function getIPData() {
  return fetch("https://api.ipify.org/?format=json");
}

// calling the function
// the function will return a promise
let promise = getIPData();

// resolving the promise to use the data
promise.then(response => response.json())
  .then(data => alert(JSON.stringify(data)))
  .catch(error => alert(error));

// other code to be executed
console.log("Other code");

Click on the Run Here button to see how the code is executed asynchronously.

Now let's understand how promises work.


How Promises work?

Promises in JavaScript are like promises in real life. When someone promises to do something, then he/she may or may not fulfill the promise.

When you call an API to fetch some data the server may or may not return the data based on the request. The server returns a promise with the status of the request, i.e. whether the request is fulfilled or not.

There are three states of a promise:

Let's see an example of promise:

Example

// creating a promise
let promise = new Promise((resolve, reject) => {
  let day = new Date().getDay();
  if (day == 0) {
    resolve("Today is Sunday");
  } else {
    reject("Today is not Sunday");
  }
});

// using the promise
promise.then((data) => {
  // .then is called when the promise is fulfilled
  alert(data);
}).catch((error) => {
  // .catch is called when the promise is rejected
  alert(error);
});

A new promise is created using the new Promise() constructor, which takes a callback function as an argument. The callback function takes two arguments resolve and reject.

The resolve argument is called when the promise is fulfilled and the reject argument is called when the promise is rejected.

Connecting the dots

When we made an API call to fetch the IP address data, the server returned a promise with the status of the request, i.e. whether the request is fulfilled or not.

We then used the .then() method to resolve the promise and the .catch() method to reject the promise.

You can see 2 .then() while calling API because the response.json() method also returns a promise that needs to be resolved to use the data.


Callback Hell

We made a promise to talk about callback hell later in this tutorial, so let's resolve the promise.😜

Callback hell is a situation when you have to make multiple asynchronous calls, then you have to nest the callback functions, which makes the code difficult to read and maintain.

Here is an example of callback hell for a file upload application:

// example of callback hell
uploadFile(file, (error, file) => {
    saveFile(file, (error, file) => {
        sendEmail(file, (error, file) => {
            logActivity(file, (error, file) => {
                addActivity(file, (error, file) => {
                  // and so on...
                });
            });
        });
    });
});

As you can see, the code is difficult to read, especially difficult to debug and maintain.

This can be solved by using promises or async/await.

Note: We will look into async/await later in this tutorial.

When you use promises, the above code will look like this:

// example of callback hell
uploadFile(file)
  .then(saveFile)
  .then(sendEmail)
  .then(logActivity)
  .then(addActivity)
  .catch(error => {
    // handle error
  });

As you can see, the code is much more readable and easy to debug and maintain.


Async/Await function (A better way to use Promises)

Async/Await is a cleaner way to use promises. It is a special syntax to work with promises more comfortably.

It makes code look synchronous while being asynchronous.

To use async/await, you need to use the async keyword before the function declaration and the await keyword before the promise.

Let's use async/await in an example:

Example

// function to make API call
async function getIPData() {
  // await the promise
  let response = await fetch("https://api.ipify.org/?format=json");
  let data = await response.json();

  // do whatever you want to do with the data
  alert(JSON.stringify(data));
}

// calling the function
getIPData();

// other code to be executed
console.log("Other code");

Conclusion

At the end of this asynchronous JavaScript tutorial, you will have learned how to make JavaScript code asynchronous using callback functions and promises.

Mastering asynchronous JavaScript is very important for a JavaScript developer, as it is used in almost every application.