Introduced in ES6, Promises allow you to easily write asynchronous code without having to deal with different callback functions. With Promises, there’s no need to worry about multi-level callback functions which are both difficult to write and maintain.
There’s another feature in JavaScript that makes it even easier to write asynchronous code using Promises: async/await functions. These allow you to write code that looks synchronous but actually performs asynchronous routines.
In this guide, we’re going to discuss what the async/await function is and how you can use it in your code. Let’s begin!
Promises: A Refresher
Before we begin talking about async/await functions, we need to recap Promises. A promise represents an asynchronous operation. It tells your code that an operation is going to be performed and, if the operation is successful, a value will be returned. Otherwise, an error will be returned to the rest of your program.
A Promise represents a value that isn’t known when the Promise is created. A Promise is just that: a promise that future values will be sent back to your code. Because Promise is an object, it must be capitalized.
Consider this scenario. You need to retrieve a resource from an API. This will take a second or two for your request to process. Instead of making the user wait for the request to process, you can move your code into a Promise so that the rest of your program can keep running.
This means that you could continue rendering part of your website’s user interface while the data is being retrieved. As soon as a value is returned, the Promise will send it to your main program.
Here’s an example of a Promise:
let sendCookies = new Promise(resolve => { setTimeout(() => { resolve("Your cookies have been sent!"); }, 1000); }); sendCookies.then(data => { console.log(data); })
Our code returns: Your cookies have been sent! When we execute the sendCookies.then() method, our Promise is initiated. Our program waits 1,000 milliseconds and then sends back the value “Your cookies have been sent!” to our main program.
How to Use Async and Await
In an async/await function, an await statement blocks your code from executing within its async function until a Promise is returned. It’s for this reason that developers often say that async/await functions look synchronous but perform asynchronous tasks.
Consider the following example:
function sendCookies() { return new Promise(resolve => { setTimeout(() => { resolve("Your cookies have been sent!"); }, 1000); }); } async function main() { const sendMessage = await sendCookies(); console.log(sendMessage); } main();
Our code returns: Your cookies have been sent! It takes 1,000 milliseconds for our sendCookies() function to return the value “Your cookies have been sent”. In this case, we have declared an async function so that our code waits for a promise to resolve or be rejected.
The “async” keyword tells our code that we want to perform an asynchronous operation in our function. The “await” keyword tells our code to wait for the return of the sendCookies() Promise before it continues executing our program.
Async functions always return a Promise.
Using Async and Await with Multiple Steps
Async/await functions are most commonly used when there are multiple Promises that you need to work with. This is sometimes called Promises chaining. This is because your code will wait for a Promise to be returned by each step before moving onto the next one:
function processOrder() { return new Promise(resolve => { setTimeout(() => { resolve("Your cookie order is being processed..."); }, 1000); }); } function sendCookies() { return new Promise(resolve => { setTimeout(() => { resolve("Your cookies have been sent!"); }, 1000); }); } async function main() { const processingMessage = await processOrder(); console.log(sendMessage); const sendMessage = await sendCookies(); console.log(sendMessage); }
Our code returns:
Your cookie order is being processed… Your cookies have been sent!
Each step takes 1,000 milliseconds to complete. Our sendCookies() function is not executed until the Promise from our processOrder() function has been returned.
Async Expressions
There are three ways you can use an async/await function.
The first way is the approach that we’ve shown in our last examples: through declaring functions. In our examples we have declared functions that return a Promise, then we have used the “async” and “await” keywords to execute those functions.
You can also declare an async function using arrow functions:
const main = async () => { const sendMessage = await sendCookies(); console.log(sendMessage); }
This code returns: Your cookies have been sent! It’s the same as our first example but instead of declaring a main() function, we have used an arrow function.
Similarly, you can use the function expression syntax:
const main = async function() { const sendMessage = await sendCookies(); console.log(sendMessage); }
This code returns: Your cookies have been sent! As you can see, the output is once again the same. The only difference is the way that we’ve declared our function.
In fact, this syntax is very similar to our last example. We’ve just used the “function()” keyword instead of using an arrow function.
There is no best way to declare an async/await function. It all depends on the program you’re writing and what syntax you prefer. While you could argue that arrow functions are the most concise method, other ways of declaring an async/await function may be better in other cases.
Processing Web Requests Using Async/Await
One of the most common usages of the async/await function is to process web requests with an API that is Promise-based, such as fetch(). You can read more about how to use the fetch() function in our beginner’s guide to JavaScript fetch.
Consider this example:
"Career Karma entered my life when I needed it most and quickly helped me match with a bootcamp. Two months after graduating, I found my dream job that aligned with my values and goals in life!"
Venus, Software Engineer at Rockbot
async function retrieveComments() { const res = await fetch('https://jsonplaceholder.typicode.com/comments'); var comments = await res.json(); comments = comments.map(comment => comment.name); console.log(comments); } retrieveComments();
Our code returns:
["id labore ex et quam laborum", "quo vero reiciendis velit similique earum" …]
When we execute our retrieveComments() function, we use the “await” keyword to wait for our fetch() statement to run. This means that the rest of our program does not continue until our web request has been processed. This is because the fetch() function returns a Promise.
When a Promise is returned by the fetch() function, we convert the value it has returned to JSON. Then we map through the object to retrieve a list of the names of all comments and print that list to the console.
How to Handle an Error
Oh, how I wish that errors didn’t happen in code. But they do, and it’s something that us developers have to plan for. In an asynchronous function, error handling is done synchronously using a try…catch statement. Consider this code:
async function retrieveCookies() { const res = await fetch('https://thisapidoesnotexist.app/cookies'); var cookieNames = await res.json(); cookieNames = cookieNames.map(cookie => cookie.name); resolve(cookieNames); } async function printCookies() { const cookies = await retrieveCookies(); console.log(cookies); } printCookies().catch(res => console.log(res.message));
Our code returns:
NetworkError when attempting to fetch resource. While our code is functional, the API we are trying to access does not exist. This means that our Promise cannot return any data, so it instead returns a rejected Promise.
In our example, we have used a .catch() statement to print out the error returned by our function if one is returned. In this case, we use reject(‘Error’) to tell our code that an error has arisen.
If this statement is executed, our .catch() statement springs into action and prints that error.
Similarly, async functions can catch syntax errors:
async function retrieveCookies() { const res = await fetch('https://thisapidoesnotexist.app/cookies'); var cookieNames = await res.json(); cookieNames = cookieNames.map(cookie => cookie.name); resolve(cookieNames); } async function printCookies() { try { const cookies = await retrieveCookies(); console.log(cookies); } catch(error) { console.log(error); } } printCookies();
Our code returns: NetworkError when attempting to fetch resource. In this example, we’ve used a try/catch statement to check if our await method has returned a successful Promise. Again, our API is not valid, which causes our program to reject our Promise. When this happens, the “catch” statement in our try/catch block is executed, which logs our error to the console.
Conclusion
Async/await functions help you write asynchronous operations in your code. When an async function is declared, you can use the “await” keyword to wait for operations to resolve. The await keyword must be used with a function that returns a Promise.
Now you’re ready to start using async/await JavaScript functions like an expert!
About us: Career Karma is a platform designed to help job seekers find, research, and connect with job training programs to advance their careers. Learn about the CK publication.