Resources
async await

Async/Await

2 contributors
ddivad#0001, veksen#1060
October 1, 2019
2 minutes read time

Async/Await

Async/Await is another way to handle the calling of asynchronous code and is built on top of Promises. It makes it possible to write asynchronous code that feels synchronous.

Its usage is equivalent to resolving a promise with .then().

It is made up of 2 keywords as the name suggests: async and await. These need to be used together for this method to work.

Async

The async keyword is used to show that the function is going to return a promise. Any return values from the function will be converted to a promise automatically, if they are not already.

async function getWeather() {
  return "sunny";
}

getWeather();
// Promise {<resolved>: "sunny"}

This basic example will return a promise with the value of getWeather.

Await

The await keyword is used to wait until a promise has executed and fetches the result. In order to use the await keyword, you need to be inside an async function.

async function checkWeather() {
  const weather = await getWeather();
  console.log(weather); // "sunny"
}

This code will call the getWeather() function from above and will wait on that line until the promise returned from the async function has returned.

Another example to demonstrate this is if we extended the getWeather() function from above to add a 5 second delay.

async function getWeather() {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("sunny"), 5000);
  });

  const weather = await promise; // Execution waits here until promise resolves
  console.log(weather); // "sunny"
}

Error Handling

As with Promises, if the Promise returns with an error, it will affect the await call that triggered it. When using Promises without Async / Await, the .catch() syntax is used, and this also exists with Aysnc / Await using the try...catch syntax. This can be demonstrated by making our foo() function throw an error.

async function foo() {
  throw new Error("An Error Occurred");
}

This would be handled using Aysnc / Await like so:

async function getData() {
  try {
    let data = await foo();
    console.log(data);
  } catch (err) {
    console.error(err); // err: An Error Occurred
  }
}

Beginner Mistakes

As mentioned in the intro, Promises are the highest source of confusion for beginners. Async / Await adds another layer on top of Promises, and comes with its own pitfalls.

await will only work if the function you try to add await to is async. Using await in top level code will not work (yet).

let weather = await getWeather(); // syntax error

To get around this issue, you can use an declare asynchronous function anonymously, if needed:

(async () => {
  let weather = await getWeather();
  console.log(weather); // "sunny"
})();

Top-Level-Await is something that may get added to Javascript in the future, but for now wrapper functions like above are needed for this functionality.

Real World Example

Here is a real function that gets character information from the Rick and Morty API. You can try it in your browser if you want to test it out.

async function getCharacters() {
  const response = await fetch(`https://rickandmortyapi.com/api/character`);
  const data = await response.json();
  return data.results;
}

// or, mixed
async function getCharacters() {
  const data = await fetch(`https://rickandmortyapi.com/api/character`).then(
    (res) => res.json()
  );
  return data.results;
}

(async () => {
  try {
    let characters = await getCharacters();
    console.log(characters); // (20) [{...}, {...}, {...}]
  } catch (err) {
    console.error(err);
  }
})();

In this example, we are querying real data from the Rick & Morty API. This API has a lot of information regarding Rick & Morty, but here, we are trying to retrieve all the characters.

  • The first thing we do is use fetch to retrieve the data from the API. fetch is a built in function in web browsers to do http requests, and returns a Promise by default. We await the result of this.
  • When we get the response, we need to get the JSON value of the data. To do this, the .json() method from fetch is used. We then return the results.
  • As await can only be used in an async function, we use the method from above to make this work. As getCharacters() returns a Promise due to it being async, we await the result. We surround this in a try...catch in case fetch returns an error. Then, if no error is returned, characters contains the information we want, and is logged to the console. If getCharacters() returns an error, that error is also logged.

Let's also look at an example from Discord.js:

client.on("message", async (msg) => {
  if (msg.author.bot) return;

  if (msg.content === "ping") {
    const message = await msg.channel.send("pong");
    message.react("⚠️");
  }
});

Comparision with default Promises

Below is the same code implemented without using Async / Await as a comparision. Here, you can see the differences between the two methods, and how Aysnc / Await makes the code appear in a more synchronous pattern, and can be clearer to follow.

function getCharacters() {
  return fetch(`https://rickandmortyapi.com/api/character`)
    .then((res) => res.json())
    .then((res) => res.results);
}

getCharacters()
  .then((characters) => {
    console.log(characters); // (20) [{...}, {...}, {...}]
  })
  .catch((err) => console.error(err));