Async/Await: A basic Introduction

Published on: 15 Aug 2017

With ES6, Promises were introduced to rescue us from what used to be called “the
callback christmas tree of sorrow” and we’re all greatful for that. Last June,
TC39 (the javascript warlords, if you don’t know them) officially released the
spec for the next version of Javascript: ES7 (or ES Next, or ES2017, or whatever
really). This new release comes packed with a tonne of the new exciting
features, including the one we’re interested in: async/await.

This feature offers a new way of addressing asynchronous tasks. It’s already
natively supported in Node.js version 8 and on the front end using a transpiler
like babel (acccording to random people on the dub-dub-dub [WWW], it doesn’t
compile efficiently, so if performance is a concern, which it should be, I
would advise against doing anything in production)

Important note: this article assumes existing knowledge of asynchronous
programming constructs and prior experience using Promises (both will help you
grasp the concept and differences between the two approaches)

Scenario

To illustrate said differences, we need a scenario: Your boss wants you to
collect data about cyberpunk novel characters (Why not ?). Thankfully, I’ve
built a RESTful API just for that purpose, it’s called wintermute . We’ll use
it along with [node-fetch][1] for making HTTP requests.

Note: This code will run inside a Node.js (v8.3.0) environment.

Ready ? Let’s dive in !

With Promises

Below is an example of how we would achieve our task using the normal Promise
construct (ES6).

constfetch=require("node-fetch")consturl="https://wintermute.design/api/characters"functiongetAllCharacters(){returnfetch(url).then(response=>response.json()).then(data=>console.log(data.characters)).catch(err=>console.error(err))}getAllCharacters()// Will log the expected data

“So what’s wrong with this piece of code ?”, I hear you asking. Well nothing
really. Now what if your boss asks you to get the associated novel for a character ?

The code would now look like this:

constfetch=require("node-fetch")consturl="https://wintermute.design/api/characters"functiongetNovelInfo(){returnfetch(url).then(response=>response.json()).then(data=>{constmolly=data.characters[2]constnovelId=molly.novelIdconstnovelInfoUrl="https://wintermute.design/api/novels/${novelId}"// Replace the quotes with backticks (``)for this to workreturnfetch(novelInfoUrl).then(response=>response.json()).then(novel=>console.log(novel))}).catch(err=>console.error(err))}getAllCharacters()// Will log the expected data

Now it’s getting a little bit more confusing, just imagine having to repeat the
process 2 or more times.

With Async/Await

Let’s look at how we can do the same thing with async/await:

constfetch=require("node-fetch")consturl="https://wintermute.design/api/characters"asyncfunctiongetAllCharacters(){constresponse=awaitfetch(url)constresponseBody=awaitresponse.json()returnresponseBody.characters}(asyncfunction(){constallCharacters=awaitgetAllCharacters()console.log(allCharacters)// Will log the expected data})()

This is easier to reason about, primarily because it reads like normal
synchronous code.

A few things to note: async functions must always be prefixed with the async
keyword and the await keyword can only be used and understood within the context
of an async function.
In this example the await declaration will wait until the Promise returned by our
fetch call is resolved and will store the content in the response variable.
Same thing for the line below, since response.json() also returns a Promise.

Hold on ! What’s up with that self invoking anonymous function at the end ?

Well, as I mentioned above, it’s impossible to use the await keyword outside on
an async context. Doing this for example wouldn’t work:

constallCharacters=getAllCharacters()// will just return a promise to be resolved using .then()constallCharacters=awaitgetAllCharacters()// will throw an error for the reason explained above

To access the data returned by our async function we must call await inside an
async function. It will make more sense in the next example.

Let’s refactor the second example where we extract the novel data for a specific
character:

constfetch=require("node-fetch")consturl="https://wintermute.design/api/characters"asyncfunctiongetAllCharacters(){constresponse=awaitfetch(url)constresponseBody=awaitresponse.json()returnresponseBody.characters}asyncfunctiongetNovelInfo(){constcharacters=awaitgetAllCharacters()constmolly=characters[2]constnovelInfoUrl="https://wintermute.design/api/novels/${molly.novelId}"// Again, replace the quotes with backticks (``)constnovelInfoResponse=awaitfetch(novelInfoUrl)constnovelInfo=awaitnovelInfoResponse.json()returnnovelInfo}(asyncfunction(){constnovelInfo=awaitgetNovelInfo()console.log(novelInfo)// Will log the expected data})()

See ? We’ve now split our logic into two separate async functions. It allowed
us to call await on getAllCharacters and manipulate the data like we wanted.
Also notice the lack of nesting here, which significantly improves readability.

Error Handling

But what about error handling ? If you look up the first example using Promises,
you can see that we used the .catch() method to capture errors if, for whatever
reason, the Promise didn’t resolve.

Part or what makes async/await so exciting is that it uses normal javascript
constructs.
In this case we can simply wrap our functions inside a try/catch block, like so:

Conclusion

Async/Await is going to make asynchronous programming easier to reason about
for intermediate/advanced programmers and help get beginners on board quicker.
Also, if you’re coming from a language that handles async tasks natively (like
golang), you’ll be right at home.

If you’ve made it this far, thanks for reading ! If you spot anything that
should be corrected, hit me up on twitter I’m @zabanaa.