Asynchronous JavaScript made readable again: async/await

Published Jun 05, 2017Last updated Jun 07, 2017
Asynchronous JavaScript made readable again: async/await

Being single threaded (at least on the frontend and disregarding the use of web workers) JavaScript makes use of asynchronous execution to keep the interface responsive.

At first we had callbacks (and 'callback hell') then we had promises that help presenting the code better with the separate catch(...) method and also by reducing the need for nesting/indentation.

ES2016 (ES7) provides us with async/await keywords that allow us to write code that seems blocking (synchronous) but is actually waiting (asynchronous).

await vs. .then(function(response) {...})

First let's focus on the await keyword.
Below we make a call that returns a promise and store the resolved value in a const. Then we pass the value to a synchronous method call.

const response  = await service.getData()
displayData(response.data)

Here is how we do it in ES2015 (ES6).

service
  .getData()
    .then(response => displayData(response.data))

With the await syntax the code is more natural to read and the flow is visible at a glance.

With the ES2015 (ES6) version the syntax is different from the synchronous code we write rest of the time. Instead of having one statement after the other, the next line of code has to be wrapped around a function (arrow function here) which will be passed to a chained method call, .then(...).

await explained

When we place the await keyword in front of the call that returns a promise here is what we are ordering the system to do:
Make the call placed right after await and wait for its response. While waiting liberate the thread and perform other operations. When the promise is resolved, come back and store this resolved value in the variable (const in our case). When you are done assigning the variable only then keep executing the next statements synchronously.

code failed

In ES2015 the way to catch errors on a promise is to add a .catch(...) statement that accepts a callback function to which it passes the error.

service
  .getData()
    .then(response => displayData(response.data))
    .catch(error => alertUser(error))

Remember that await make our code look very similar to synchronous code. Thus catching errors is written the same way as with synchronous statements.

try {
  const response  = await service.getData()
  displayData(response.data)
}
catch (error) {
  alertUser(error)
}

async: where do I place it?

The function where that has the await keyword in its statement(s) must be marked async

async function init() {
  const response  = await service.getData()
  displayData(response.data)
}

Readable Asynchronous JavaScript

There you have it: async/await help you write asynchronous code with synchronous look & feel syntax. Asynchronous that is easier to write and easier to reason about.
We write code for other developers, thus I hope this syntax helps you write more understandable code, making your team's life easier.

Let me know if that helps. Suggestions and critiques are most welcome.
Happy coding! 😉

Discover and read more posts from Cedric Poilly
get started
Enjoy this post?

Leave a like and comment for Cedric

11
2
2Replies
Ben Fellows
4 months ago

Hi Cedric,

Nice post.

Just a question.

Can you just clarify on the last bit about async, when you are returning a value using async function, would you just return values as a normal synchronous piece of code? So no callbacks required?

async function init() {
  const response  = await service.getData()
  displayData(response.data)
}

would become

async function init() {
  const response  = await service.getData()
  return response.data
}

Thanks,

Ben

Cedric Poilly
3 months ago

Hi Ben,

displayData(response.data) is not the actual code, I just used it to express the meaning.
What I normally do is that I return the whole response variable, and then where I’m calling init() I would then doresponse.json() (or response.text(), etc, depending on the nature of the value) to extract the data.

Here’s how it goes:

// file 1, defining `init()`

async function init() {
  return await service.getData() // returns a promise
}

...

// file 2, calling `init()`

async function start() {
  const response = await init()
  const data = reponse.json()
}

I hope that helps. Thank you for your input. Based on it I might update the article or write another one.

Cheers

Get curated posts in your inbox

Read more posts to become a better developer