Blog's View

Understanding behaviour of JS Async/Await

January 3, 2024
Penned By -
Saurabh Kumar

Async/Await are keywords from JS. It is generally used in asynchronous programming.

You probably will need to use Async/Await in two scenarios.

  1. When dealing with asynchronous scenarios
  1. As a replacement for promise chaining

At first, we will understand what is asynchronous and synchronous.

Synchronous tasks are those which are bound to happen one after another. So, here 2nd(later) task gets dependent on 1st task’s completion.

Asynchronous tasks are totally opposite of synchronous tasks. Unlike synchronous tasks, 2nd(later) task does not depend on 1st task’s completion.

E.g.: I’ll take example of food order

Synchronous: You are working from home. you go to restaurants, order food, and wait for food to be prepared once prepared and served you eat and then you return home.

Asynchronous: You are working from home and at some point, of time You order food. You continue working food gets delivered and then you eat.

In the first example you had to stop your work completely until you finished eating and get back home.

In this example eating food task is completed dependent on prior task which are sequenced as going to restaurant, ordering, and eventually waiting.

In the second example you order food and resumed your work till the food gets delivered.

In this example your eating is not dependent on the previous task which is doing work. And the sequence goes like Work, Order food, Work, Food arrives and finally you eat.

You can read about promise chaining using this link

Using promises - JavaScript | MDN (mozilla.org)

Understanding the keywords and their responsibility

Async keyword is used to declare a function which returns a new Promise every time it is called, which will be resolved with the value returned by the async function or rejected with an exception uncaught within the async function [1].  

Await keyword is used to call async function. It stops the current function or code execution until the returned promise is fulfilled or rejected.

Understanding promises

We know that async function returns promises which then eventually fulfills as resolve or reject.

We need to understand what promise actually is.

I will take the example of food ordering again

  1. You placed the order for the food, then you receive a promise that indicates that food is being prepared and will be delivered.
  1. Now you are free to do another task
  1. Once the food is ready and delivered, the promise is resolved or fulfilled, and you receive your food
  1. If for some reason the food cannot be delivered (e.g., Restaurant is out of ingredients), the promise is rejected, and you are notified of the issue.

I hope this clears the Promise concept.

Understanding behaviors of Async/Await through scenarios

Scenario 1: Calling Async function in Asynchronous way

In this scenario we call the Async function without using await keyword. Function returns the promise with “pending” state immediately and rest of code below the function call continues running. When promise eventually gets fulfilled or rejected it updates the promise state as “fulfilled” or “rejected” respectively. In this way one or multiple independent call can be done without disturbing main flow.

Scenario 2: Calling Async function in synchronous way

In this scenario we call the Async method with await keyword. Function returns the promise with pending state immediately and await keyword makes the code wait for the promise status to be settled (rejected or fulfilled). After the promise gets settled rest of the code continues running. In this way if a code written below the await call depends on the return value of the function will get the value.

Scenario 3: Calling multiple Async functions parallelly but while doing parallel calls code should wait for all promise settlements.

This might be confusing to many of you, so I’ll explain it a bit further with an example. Let’s say you query multiple databases hosted on different machine because you want to do the reconciliation. In this scenario you will have to query two databases parallelly to save time. Here your reconciliation code should not work until both query runs successfully and gets the data. I hope you have understood the scenario. Now I’ll go to the solution part. To handle this scenario promise object has three methods

  1. Promise.all() – Returned promise fulfills when all the input's promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values. It rejects when any of the input's promises rejects, with this first rejection reason [2].
  1. Promise.allSettled() – Returned promise fulfills when all the input's promises settle (including when an empty iterable is passed), with an array of objects that describe the outcome of each promise [3].

These method takes iterable(array) of promises as input and returns a single Promise. You can read about these methods and how to use them on Promise - JavaScript | MDN (mozilla.org).  

Conclusion

In conclusion, understanding the behaviour of async/await in JavaScript is crucial for effective asynchronous programming. Async/await provides a more readable and simplified approach compared to promise chaining. By using the async keyword, we can declare functions that return promises, which can be resolved or rejected based on their execution. The await keyword allows us to pause the execution of code until a promise is fulfilled or rejected, ensuring that dependent tasks are handled correctly.

Promises play a significant role in asynchronous programming, acting as a placeholder for future values. They allow us to handle tasks independently and receive the results once they are ready. Whether the promise is fulfilled or rejected, we can handle the outcome accordingly.

Through different scenarios, we explored the practical implementation of async/await. Calling an async function asynchronously allows us to continue executing code without waiting for the promise to be fulfilled. On the other hand, calling an async function synchronously with the await keyword ensures that the code waits for the promise to settle before proceeding.

In situations where parallel calls to multiple async functions are required, we can utilize methods like Promise.all() or Promise.allSettled() depending on your requirement of all promises resolve or all promises settle respectively before moving forward. These methods provide effective ways to handle scenarios where multiple independent tasks need to be completed before proceeding.

Reference

[1] async function - JavaScript | MDN (mozilla.org)

[2] Promise.all() - JavaScript | MDN (mozilla.org)

[3] Promise.allSettled() - JavaScript | MDN (mozilla.org)