Async/Await in JavaScript Async/Await in JavaScript

Async functions and Await keyword are latest additions in JavaScript as part of ECMAScript 2017 release which introduced a new way of writing asynchronous functions. In this post we will talk about why we should use async/wait, its syntax and practical usage with example.

Why Async/Await?

In earlier days, you would have used callbacks to handle asynchronous operations. However, callbacks have limited functionality and often leads to unmanageable code if you are handling multiple async calls, it leads to heavily nested callback code which is also known as callback hell.

Later, Promises were introduced in ES6 to overcome the problems of callback functions and improved code readability. Finally Async/Await introduced in ES2017, which are nothing but the syntactic improved version of promises. Its underlying is Promise with improved syntax which provides,

  • better way to chain promises and pass values between chained promises
  • more concise and readable code compare to promises
  • debugging is easy
  • better error handling

Also read this post for more details on promises in javascript

Syntax

async

When async keyword is applied before a function, it turns into asynchronous function and always return a promise object.

1async function hello() {
2  //return Promise.resolve("Hello");
3  return "Hello";
4}
5
6console.log(hello());
7hello().then(data => console.log(data));
Output
Promise {<resolved>: "Hello"} Hello

We see in above code snippet that when we execute async function hello(), it wraps the string value in promise object and return a resolved promise. We can also explicitly return the resolved promise object, line 2 and line 3 are same.

We further used then on resolved promise object to get Hello string (line 7)

await

// works only inside async functions
let value = await promise;

await keyword works only inside async function and it makes the function execution wait until the returned promise settles (either resolve or reject).

 1async function hello() {
 2  let promise = new Promise((resolve, reject) => {
 3    setTimeout(() => resolve("Hello"), 5000)
 4  });
 5
 6  let value = await promise; // wait until the promise resolves
 7
 8  return value;
 9}
10
11hello().then(data => console.log(data));

Please note that in above code snippet when we execute async function hello(), function execution literally waits for 5s at line 6 before returning resolved promise object. CPU resources are not utilized in this wait period and can be used for other work.

Also note that if you use await keyword inside non-async function, it returns SyntaxError like below:

function hello() {
  let promise = Promise.resolve("Hello");
  let value = await promise; ⓧ Uncaught SyntaxError: await is only valid in async function
  return value;
}

Usage

Let’s see one of the practical example of getting data from multiple HTTP endpoints using fetch API.

1. Create three promise objects

We have created a common function getData and used this to create three parameterized promise objects getUser, getPosts and getComments to fetch data from their respective HTTP endpoint.

//create a common getData function
let getData = (url) => new Promise(function (resolve, reject ){
  fetch(url)
    .then(response => {
        return response.json();
    })
    .then(data => {
        resolve(data);
    })
    .catch(error => {
        reject(error);
    });
});

//create multiple promises from common getData function
let getUsers = getData('https://jsonplaceholder.typicode.com/users');
let getPosts = (userId) => getData(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);
let getComments = (postId) => getData(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`);


2. Promise Chaining

Our goal is to fetch all comments on first post of first user.

We are first fetching all users from getUsers promise and chaining it with getPost promise by passing firstUser. Further chaining it with getComments promise by passing firstPost.

//promise chaining of multiple asynchronous calls
getUsers.then(users => {
  let firstUser = users[0];
  return getPosts(firstUser.id);
}).then(posts => {
  let firstPost = posts[0];
  return getComments(firstPost.id);
}).then(comments => {
  console.log(comments);
}).catch(error => console.error(error));
Output
▼ (5) [{…}, {…}, {…}, {…}, {…}] ➤ 0: {postId: 1, id: 1, name: "id labore ex et quam laborum", email: "Eliseo@gardner.biz", body: "laudantium enim quasi est quidem magnam voluptate …utem quasi↵reiciendis et nam sapiente accusantium"} ➤ 1: {postId: 1, id: 2, name: "quo vero reiciendis velit similique earum", email: "Jayne_Kuhic@sydney.com", body: "est natus enim nihil est dolore omnis voluptatem n…iatur↵nihil sint nostrum voluptatem reiciendis et"} ➤ 2: {postId: 1, id: 3, name: "odio adipisci rerum aut animi", email: "Nikita@garfield.biz", body: "quia molestiae reprehenderit quasi aspernatur↵aut …mus et vero voluptates excepturi deleniti ratione"} ➤ 3: {postId: 1, id: 4, name: "alias odio sit", email: "Lew@alysha.tv", body: "non et atque↵occaecati deserunt quas accusantium u…r itaque dolor↵et qui rerum deleniti ut occaecati"} ➤ 4: {postId: 1, id: 5, name: "vero eaque aliquid doloribus et culpa", email: "Hayden@althea.biz", body: "harum non quasi et ratione↵tempore iure ex volupta…ugit inventore cupiditate↵voluptates magni quo et"} length: 5 ➤ __proto__: Array(0)


3. async/await

Let’s achieve the same goal of fetching comments using async/await,

//async and await makes code cleaner and readable
async function getCommentsOfFirstPostByFirstUser(){
  let users = await getUsers;
  let firstUser = users[0];
  let posts = await getPosts(firstUser.id);
  let firstPost = posts[0];
  let comments = await getComments(firstPost.id);
  return comments;
}

getCommentsOfFirstPostByFirstUser().then(comments => console.log(comments));
Output
▼ (5) [{…}, {…}, {…}, {…}, {…}] ➤ 0: {postId: 1, id: 1, name: "id labore ex et quam laborum", email: "Eliseo@gardner.biz", body: "laudantium enim quasi est quidem magnam voluptate …utem quasi↵reiciendis et nam sapiente accusantium"} ➤ 1: {postId: 1, id: 2, name: "quo vero reiciendis velit similique earum", email: "Jayne_Kuhic@sydney.com", body: "est natus enim nihil est dolore omnis voluptatem n…iatur↵nihil sint nostrum voluptatem reiciendis et"} ➤ 2: {postId: 1, id: 3, name: "odio adipisci rerum aut animi", email: "Nikita@garfield.biz", body: "quia molestiae reprehenderit quasi aspernatur↵aut …mus et vero voluptates excepturi deleniti ratione"} ➤ 3: {postId: 1, id: 4, name: "alias odio sit", email: "Lew@alysha.tv", body: "non et atque↵occaecati deserunt quas accusantium u…r itaque dolor↵et qui rerum deleniti ut occaecati"} ➤ 4: {postId: 1, id: 5, name: "vero eaque aliquid doloribus et culpa", email: "Hayden@althea.biz", body: "harum non quasi et ratione↵tempore iure ex volupta…ugit inventore cupiditate↵voluptates magni quo et"} length: 5 ➤ __proto__: Array(0)

Summary

We see that async/await are much easier to use as compare to promises.