Introduction to async/await keywords

Exploring the async/await keywords, as well as comparing them to .then()/.catch()

Published July 24, 2022

Asynchronous JavaScript and Promises

Before diving into async/await, you must first have a strong grasp of asynchronous programming. If you're unfamiliar with the concept, you can check out an article I wrote about it here:

...furthermore, since async/await deals with Promises, you should also have a solid understanding on when, how, and why to use Promises. This article serves as a continuation to the previous article I wrote, which can be found here. With that out the way, we can finally dive into async/await.

What is async/await?

The async/await keywords were first introduced in ES7 as a way to enable asynchronous, promise-based behavior. They serve as an alternative to the .then()/.catch() methods used in ES6, all while providing a cleaner and more concise syntax that resembles synchronous code.

async keyword

Take a look at this arrow function:


const sayHi = () => {
  return "hi!";
};

sayHi();
// hi
        

...to no surprise, hi is logged to the console. What if we converted this regular function to an asynchronous function? This can be done by adding the async keyword, which is used to declare asynchronous functions...


const sayHi = async () => {
  return "hi!";
};
sayHi();
// Promise {fulfilled: 'hi'}
        

...since sayHi is now an asynchronous function, it returns a Promise, in this case, a fulfilled Promise that holds the value of hi. Consuming this value is nothing new and can easily be achieved with a simple .then() callback...


const sayHi = async () => {
  return "hi";
};

sayHi().then((greeting) => {
  console.log(greeting);
});
// hi
        

...this works but you would only end up using .then() and .catch() again, which defeats the whole purpose of async. Instead, let's combine it with the await keyword.

await keyword

Unlike the async keyword which is used only once outside a function, the await keyword can be used multiple times inside asynchronous functions. await will pause further code execution until the Promise settles and returns a result. This is extremely useful when doing things like making API requests, where there's a chance that the data won't be returned immediately. As an example, I'll use the setTimeout() method, which will serve as a mock API request:


const sayHi = async () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("hi");
    }, 3000);
  });

  let greeting = await promise;

  console.log(greeting);
};

sayHi();
// hi
        

...here, an asynchronous function sayHi is created. Inside the function is a promise that resolves with a value of hi after 3 seconds. We can pretend this is a slow API request. Since the data inside promise takes a while to arrive, we must wait until the Promise settles before continuing code execution. In order to do this, we use the await keyword. Do note: JavaScript execution will only pause inside said asynchronous function; other tasks and events will continue to be executed, making it super efficient and resourceful.

Handling errors

It'd be nice if programs alway ran without any errors, but this isn't the case. Sometimes you'll encounter errors, so it's important to know how to deal with them. Luckily, errors can easily be caught by using the try...catch statement.

The try...catch statement contains two blocks: try and catch. Anything inside the try block will be executed first, and if an error occurs, the catch block will fire. Here's a more realistic example using the Fetch API to grab data from a nonexistent endpoint:


const someData = async () => {
  try {
    const response = await fetch("https://some-endpoint");
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.log(err);
  }
};

someData();
// TypeError: Failed to fetch
        

...when someData is invoked, JavaScript will first attempt to access the endpoint, return the data, and log it; if that fails, an error will be logged to the console. Since the used resource is fake, the try block will fail and fire the catch block.

Although async/await is used for asynchronous operations, the actual syntax looks synchronous, which makes it super clean and concise. Additionally, it also helps get rid of promise chaining altogether. Despite this, you should still know how to use both methods in case you ever need to use one or the other.