async & await
JavaScript promises were a revelation in JavaScript, providing an alternative to the JavaScript callback hell we'd quickly found ourselves in. Promises also allowed us to introduce and better handle asynchronous tasks. While promises were an improvement over callback hell, they still require lots of then
s which can become messy. I've really taken to ES2017's async
and await
keywords for simplifying promise handling. Let's have a look at async
and await
!
Quick Basics
async
is a keyword for the function declarationawait
is used during the promise handlingawait
must be used within anasync
function, though Chrome now supports "top level"await
async
functions return a promise, regardless of what thereturn
value is within the functionasync
/await
and promises are essentially the same under the hood- Available now in most browsers as well as Node.js
Benefits of async
and await
- Your code is more simplistic, precise
- Debugging is easier thanks to less callbacks
- Conversion from promise
then
/catch
code is easy - Your code can be more "top down", less nesting
Basic async
and await
Illustration
It's always easiest to learn from an example, so let's have a look at a very simple async
/ await
usage:
// Function declared as async so await can be used async function fetchContent() { // Instead of using fetch().then, use await let content = await fetch('/'); let text = await content.text(); // Inside the async function text is the request body console.log(text); // Resolve this async function with the text return text; } // Use the async function var promise = fetchContent().then(...);
Start by declaring the function as async
; this declaration allows await
to be used from within. The await
keyword is then followed by a promise-yielding action, which of course the fetch
API is. The asynchronous routine (fetch
in this case) runs and execution of further code halts (though not blocking) until the async action finishes. The function then resolves with the return
value and a promise is returned.
Essentially you get to keep your code "inline" without the need for callbacks. It's async made a bit more simple!
Converting Promise Handling to await
There's a good chance you'll want to update your promise code when time becomes available. Let's walk through updating promise to await
:
// Before: callback city! fetch('/users.json') .then(response => response.json()) .then(json => { console.log(json); }) .catch(e => { console.log('error!'); }) // After: no more callbacks! async function getJson() { try { let response = await fetch('/users.json'); let json = await response.json(); console.log(json); } catch(e) { console.log('Error!', e); } }
The conversion from loads of then
s to await
is simple to execute and your code hopefully looks a bit maintainable!
async
/ await
Patterns
There are a number of ways you can declare async
functions.
Anonymous Async Function
let main = (async function() { let value = await fetch('/'); })();
Async Function Declaration
async function main() { let value = await fetch('/'); };
Async Function Assignment
let main = async function() { let value = await fetch('/'); }; // Arrow functions too! let main = async () => { let value = await fetch('/'); };
Async Function as Argument
document.body.addEventListener('click', async function() { let value = await fetch('/'); });
Object & Class Methods
// Object property let obj = { async method() { let value = await fetch('/'); } }; // Class methods class MyClass { async myMethod() { let value = await fetch('/'); } }
As you can see, adding async
is really easy and accommodates all function creation workflows!
Error Handling
Traditional promise use allows you to use a catch
callback to handle rejection. When you use await
, your best bet is using try
/catch
:
try { let x = await myAsyncFunction(); } catch(e) { // Error! }
The old try
/catch
isn't as glamorous as a promise's catch
callback but is just as effective.
Parallelism
Google's Jake Archibald make excellent points in the Async functions document about not getting too sequential with your await
s. The idea is to avoid stacking awaits, when possible, and instead trigger tasks immediately and use await
after said tasks are triggered:
// Will take 1000ms total! async function series() { await wait(500); await wait(500); return "done!"; } // Would take only 500ms total! async function parallel() { const wait1 = wait(500); const wait2 = wait(500); await wait1; await wait2; return "done!"; }
The first block is bad because the second wait
happens after the the first wait
completes. The second block is a better method: trigger both wait
calls and then use await
; doing so allows the async functions to happen concurrently!
Promise.all
Equivalents
One of my favorite functions of the Promise API is Promise.all
, which fires a callback when all fetches are complete. There's no direct async
/ await
equivalent but this post provides a good equivalent:
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
Remember that async
/ await
are essentially the same as promises deep down, so we're simply awaiting the aggregated promise to be resolved!
You can now use async
and await
in all major browsers. These new keywords are also available within Node.js; older Node.js versions can use the transform-async-to-generator
babel plugin to use async
and await
today. Promises are still excellent but are made more maintainable with async
and await
!
Hey David,
Long time fan of the blog,
Question about the alternative for
Promise.all
– if we would define multiple async methods and await them one after the other like in your parallelism example wouldn’t any following code wait for all promises to resolve likePromise.all
?Thanks
Hi Shahar,
With the alternative, all of the requests are made asynchronously. The requests are fired of sequentially, but the code doesn’t stop to wait for them to resolve until the
await
statement.If you want to call two async functions and have them run in parallel, then assign the function call to a variable then on a new line, await that variable.
Awesome blog post about this new ES2017 feature. I love the new way of writing async functions in a more synchronous way with async – await. We are on a good way to let the callback hell crap behind us :) I have recently published a blog post about the transition of JS from traditional callbacks to async-await. If you are interested, you can check it out under https://www.jratzenboeck.com/web/2018/01/06/exiting-callback-hell.html
Can async/await be used in a for statement like :-
You can only tag functions as async which means that they return a promise and can be awaited.
Awesome post! Very clarifying!
But I came here trying to figure out somethign that could even sound silly.
I’ve been working on a ReactJS project in the last few weeks and a doubt came up.
My coworker asked me if calls to an API using
await
are render blocking.e.g.:
We have an module that we call inside the
server.js
file to trigger that query on the server side.Is that even a good practice? I know this post isn’t about ReactJS but was the best one I found about
async
/await
.Cheers!
Don’t you need to throw an exception if you’re using
await fetch(url)
in atry
catch
statement?meaning
await fetch(url)
will always return a promise regardless of what it returned to youdoes anyone have information on async phone number address etc
useful post.. however does’t explain how to use async/wait in loop