Promises give us an easier way to deal with asynchrony in our code in a sequential manner. Considering that our brains are not designed to deal with asynchronicity efficiently, this is a much welcome addition. Async/await functions, a new addition with ES2017 (ES8), help us even more in allowing us to write completely synchronous-looking code while performing asynchronous tasks behind the scenes.
The functionality achieved using async functions can be recreated by combining promises with generators, but async functions give us what we need without any extra boilerplate code.
In the following example, we first declare a function that returns a promise that resolves to a value of 🤡
after 2 seconds. We then declare an async function and await for the promise to resolve before logging the message to the console:
await
is a new operator used to wait for a promise to resolve or reject. It can only be used inside an async function.
The power of async functions becomes more evident when there are multiple steps involved:
A word of caution however, in the above example each step is done sequentially, with each additional step waiting for the step before to resolve or reject before continuing. If you instead want the steps to happen in parallel, you can simply use Promise.all to wait for all the promises to have fulfilled:
Promise.all returns an array with the resolved values once all the passed-in promises have resolved.
In the above we also make use of some nice array destructuring to make our code succinct.
Async functions always return a promise, so the following may not produce the result you’re after:
Since what’s returned is a promise, you could do something like this instead:
…or just this:
So far with our examples we saw the async function as a function declaration, but we can also define async function expressions and async arrow functions:
Here’s the async function from our first example, but defined as a function expression:
Here’s that same example once again, but this time defined as an arrow function:
Something else that’s very nice about async functions is that error handling is also done completely synchronously, using good old try…catch statements. Let’s demonstrate by using a promise that will reject half the time:
Given that async functions always return a promise, you can also deal with unhandled errors as you would normally using a catch statement:
This synchronous error handling doesn’t just work when a promise is rejected, but also when there’s an actual runtime or syntax error happening. In the following example, the second time with call our msg function we pass in a number value that doesn’t have a toUpperCase method in its prototype chain. Our try…catch block catches that error just as well:
As we showed in our primer to the Fetch API, web APIs that are promise-based are a perfect candidate for async functions:
Browser Support: As of 2020, 94% of browsers worldwide can handle async/await in javascript Notable exceptions are IE11 and Opera Mini.
Before Async/await functions, JavaScript code that relied on lots of asynchronous events (for example: code that made lots of calls to APIs) would end up in what some called “callback hell” - A chain of functions and callbacks that was very difficult to read and understand.
Async and await allow us to write asynchronous JavaScript code that reads much more clearly.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Please fix the typo: “but you we can also”