DEV Community

Cover image for Master Async/Await in JavaScript: A Practical Guide for Asynchronous Programming
Cameron Lucas
Cameron Lucas

Posted on

Master Async/Await in JavaScript: A Practical Guide for Asynchronous Programming

Async/await is a language feature introduced in JavaScript with the ES2017 release that enables asynchronous, non-blocking behavior in JavaScript. It is built on top of promises, providing an easier and cleaner way to handle asynchronous code. Async/await makes it possible to write asynchronous code that looks and behaves like synchronous code, making it easier to read and understand.

In this blog post, we will go over the basics of async/await and how it works, as well as some practical examples of how to use it in your code.

What is Async/Await?

Async/await is a syntax for dealing with asynchronous code in JavaScript. It allows you to write asynchronous code that looks and behaves like synchronous code, using the async and await keywords.

Here's a simple example of async/await in action:

async function getData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
}
Enter fullscreen mode Exit fullscreen mode

The async keyword indicates that the function is asynchronous and returns a promise. The await keyword is used to wait for the promise to resolve before moving on to the next line of code. In the example above, the await keyword is used to wait for the fetch call to complete and for the response.json() call to resolve.

Async/await makes it possible to write asynchronous code that looks and behaves like synchronous code, making it easier to read and understand. It also makes it easier to write and maintain asynchronous code, since you don't have to deal with the complexities of promises.

How Does Async/Await Work?

Async/await is built on top of promises, and it relies on the Promise.prototype.then() method to handle the asynchronous behavior.

Here's a breakdown of how async/await works:

When an async function is called, it returns a promise.
Inside the async function, you can use the await keyword to wait for a promise to resolve.
If the promise resolves, the value of the promise is returned. If the promise is rejected, an error is thrown.
Here's an example of an async function that uses the await keyword to wait for a promise to resolve:

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(error);
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the await keyword is used to wait for the fetch call to complete and for the response.json() call to resolve. If the promise is rejected (for example, if there is an error making the fetch call), the error is caught in the catch block and logged to the console.

Using Async/Await

Now that we have a basic understanding of how async/await works, let's look at some practical examples of how to use it in your code.

Making Asynchronous HTTP Requests

One common use case for async/await is making asynchronous HTTP requests. Using async/await makes it easy to perform multiple HTTP requests in parallel, and to wait for the results before moving on to the next step in your code.

Here's an example of using async/await to make multiple HTTP requests in parallel:

async function getData() {
  const [response1, response2, response3] = await Promise.all([
    fetch('https://api.example.com/data1'),
    fetch('https://api.example.com/data2'),
    fetch('https://api.example.com/data3')
  ]);
  const data1 = await response1.json();
  const data2 = await response2.json();
  const data3 = await response3.json();
  console.log(data1, data2, data3);
}
Enter fullscreen mode Exit fullscreen mode

In this example, we are using the Promise.all() method to make multiple HTTP requests in parallel. The await keyword is used to wait for all of the promises to resolve before logging the resulting data to the console.

Handling Errors

It's important to handle errors when using async/await, just like with any other asynchronous code. To handle errors in an async function, you can use the try and catch keywords.

Here's an example of how to handle errors in an async function:

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(error);
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the try block contains the code that might throw an error, and the catch block contains the code that will handle the error. If an error is thrown in the try block, it will be caught in the catch block and logged to the console.

Chaining Async Functions

Async functions can be chained together just like promises. This can be useful when you need to perform multiple asynchronous operations in a specific order.

Here's an example of chaining async functions:

async function getData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
}

async function processData(data) {
  // do something with the data
  return processedData;
}

async function displayData() {
  const data = await getData();
  const processedData = await processData(data);
  console.log(processedData);
}

displayData();
Enter fullscreen mode Exit fullscreen mode

In this example, we have three async functions: getData, processData, and displayData. The displayData function calls the getData function and waits for it to resolve before calling the processData function and waiting for it to resolve. The resulting processed data is then logged to the console.

Async/Await vs Promises

Async/await is built on top of promises, and it provides a cleaner and easier way to write asynchronous code that uses promises. However, there are still situations where you might want to use promises directly instead of async/await.

One reason to use promises directly is if you need to use features of the Promise API that are not available with async/await. For example, you might want to use the Promise.prototype.finally() method to execute code after a promise is resolved or rejected, regardless of the outcome. This can be useful for cleaning up resources or logging the results of a promise.

Here's an example of using the finally() method with a promise:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    // do something with the data
  })
  .catch(error => {
    console.error(error);
  })
  .finally(() => {
    console.log('request complete');
  });
Enter fullscreen mode Exit fullscreen mode

In this example, the finally() method is called after the promise is either resolved or rejected, and it logs a message to the console.

Another reason to use promises directly is if you need to work with older browsers that do not support async/await. In this case, you can use a polyfill to add support for async/await to older browsers.

Conclusion

Async/await is a powerful language feature that makes it easier to write and understand asynchronous code in JavaScript. It is built on top of promises, providing a cleaner and easier way to handle asynchronous behavior. With async/await, you can write asynchronous code that looks and behaves like synchronous code, making it easier to read and understand.

I hope this blog post has helped you understand async/await and how to use it in your code. If you have any questions or comments, please leave them in the comments section below.

Top comments (1)

Collapse
 
webjose profile image
José Pablo Ramírez Vargas • Edited

I Bet You've Been Doing Async/Await Wrong

I leave this here for readers to double-check. This does not seem to be your case, though. Your async/await looks fine, making 3 parallel requests using Promise.all.