How to Use Async/Await in JavaScript

How to Use Async/Await in JavaScript


About the author

@catalinmpit is a software engineer, AWS community builder and technical writer based out of London. He’s currently an engineer at TypingDNA, working on applying keystroke dynamics as a means of biometrics authentication.

Check out more of his work on catalins.tech


`async/await` was introduced in ECMAScript 2017
`async/await` was introduced in ECMAScript 2017

The purpose of this article is to teach you how to work with promises by using async/await. Why would you want to use async/await, though? The most significant benefit is that it makes the code more readable and cleaner by removing the promise chains.

Let’s see what async/await is and how to use it.

The usual promise

Let’s start off with an example of a promise in JavaScript:

fetch("https://api.app/v1/users/cp"); // dummy URL
.then(response => response.json());
.then(console.log);

This probably looks familiar. Now, let’s see the same code but re-written with async/await.

async function getData() {
    const response = await fetch("https://api.app/v1/users/cp/avatar"); // dummy URL
    const data = response.json();

    console.log(data);
}

Which one do you prefer? In my opinion, using async/await gives a clearer understanding of the code.

What async/await is

async/await is built on top of promises, and it allows us to write asynchronous code better. It is a new way of writing asynchronous code instead of using promises and callbacks.

The power of async/await is that it organizes the code making it look cleaner and more “synchronous.”

There are two particular keywords:

  • async
  • await

We use the keyword async at the beginning of the function. Using the async keyword means the function always returns a promise. Also, if we want to use the keyword await inside a function, that function must always start with the keyword async. The code below returns a promise when we call the greeting function. The promise object contains a state (fulfilled or rejected) and a result (in this case, a string).

async function greeting(name) {
    return `Hello, ${name}!`;
}

Secondly, we can use the keyword await to wait until a promise fulfills and then return the result.

async function greeting(name) {
    return greet = await Promise.resolve(`Hello, ${name}!`);
}

greeting("Catalin").then(console.log); // Returns "Hello, Catalin!"

The above example might not be useful, but imagine when you make an API call (see the example above, in the first section of the article).

Other benefits

Better error handling

Using async/await allows us to handle the synchronous code and asynchronous code in the same construct. Now if there is an error in resolving the promise, the control jumps to the catch block to handle the error.

You can even wrap multiple promises in the same try block, and the code will catch the errors for all the promises, not just one. It also tells you where the error occurred, in which promise.

async function getUserInfo() {
    try {
        const response = await fetch("https://api.app/v1/users/cp/avatar"); // dummy URL
        const data = response.json();

        console.log(data);
    } catch (err) {
        console.log(err);
    }
}

In the above code snippet, the code jumps to the catch block if there is an error.

Better code

async/await allows us to write clear and more understandable code by making it look more like synchronous code. You can see how much cleaner the code is by looking at “The usual promise” section.

Conditionals

Another benefit of using async/await is that it makes it easier to work with conditionals.

The code snippets below are basic, and they do not handle errors. Their sole purpose is to show the difference between promises and async/await when using conditionals.

const getUserInformation = () => {
    return getUser()
    .then(user => {
      if (user.profile) {
        return getUserProfile(user.id)
        .then(userProfile) {
          console.log(userProfile);
          return userProfile;
        }
      } else {
        console.log(user);
        return user;
      }
    });
};

The above snippet illustrates using conditionals in a promise. Even though the code is not complex, it is difficult to read. Now imagine how complex the code would be if we had more if statements and requests.

Below, you can see the code re-written using async/await.

const getUserInformation = async () => {
  const user = await getUser();

  if (user.profile) {
    const userProfile = await getUserProfile(user.id);

    console.log(userProfile);
    return userProfile;
  } else {
    console.log(user);
    return user;
  }
};

Doesn’t it look better? You can see how converting promises to async/await improves your code’s readability by a significant margin making it easier to understand.

Conclusion

  • Adding async to a method header means you’ll always return a promise.
  • Using async also allows you to use the await keyword, which allows you to wait until a promise is resolved.
  • Using the await keyword blocks the code from executing until the promise is resolved or rejected. If the promise cannot settle, it generates an exception you can deal with.
  • async/await makes the code more explicit, easier to understand, and more concise.

Catalin regularly posts helpful development tips and guides on Twitter. Be sure to follow him at @catalinmpit


About PullRequest

PullRequest is a platform for code review, built for teams of all sizes. We have the world's largest network of on-demand reviewers, backed by best-in-class automation tools. Because code quality is important.

Learn more about PullRequest

Catalin Pit headshot
by Catalin Pit

October 14, 2020