Understanding async await

Async await is easily one the best new features in Javascript and now that it’s available natively in node there’s no reason not to dive in!

It’s fairly simple to dive in and start using straight away but I feel that some people might have missed the core concepts which I think are fundamental to using async await so wanted to write an article detailing these in the terms I understand them.

One common misconception is that async await is an alternative to promises, but this is fundamentally untrue, it’s an accompaniment to promises not a replacement and it’s actually critical to understand promises in depth to make sense of async await, so consider that a prerequisite! Now let’s continue…

What does await do?

Basically it allows you to pause execution (in a non blocking way) until a promise resolves, and then act accordingly.

  • When used on a promise that resolves, it will convert the promise resolving where you would normally use .then() to access the returned value.

similar to…

Using await allows a much nicer flatter structure that just looks like synchronous code.

  • When used on a promise that rejects, it will convert that promise rejection into a thrown that error


However, you can also only use the await keyword inside an async function, so lets talk about those now…

What does an async function do?

The best way to think of an async function is that it will always return a promise, even if you don’t actually return one!

  • If inside an async function a value is returned, externally that will be as if the function returned a promise that resolves with that value you returned.
  • If inside an async function an error is thrown, externally that will be as if the function returned a promise that rejects with that error.

This is equivalent to…

Both of these functions will return a promise that resolves to provide the .value property of the result from someAsyncThing().

But what if someAsyncThing() instead rejects instead of resolves?
Lets imagine it rejects with the error new Error(‘Boom’)

As you can see in this example, things are more complex than they appear…

  • we have a rejected promise,
  • that gets turned into a thrown error,
  • that gets turned back into a rejected promise,
  • that gets turned back into a thrown error.

But as long as you understand the flow of how promises are converted to returned values / thrown errors with await, and the how returned values and thrown errors are converted to promises within async functions, the flow makes sense.

Async functions are safer too!

One final takeaway is that async functions are safer, with a lower risk of errors being swallowed. Because if you throw an error anywhere in the code, it will return a rejected promise.

Where as with non async functions you must be careful. Errors will only be wrapped in a rejected promise if the error is thrown within the promise chain, but not if it is thrown before hand. For example….

This function will ensure that if fetchResults errors we can handle it within our catch statement. But what would happen if data wasn’t an object? So the .page property couldn’t be accessed?

In that case an error would get thrown, and because this occurs outside of the promise chain, fetchStuff would also throw the error meaning our catch statement never gets invoked (since no rejected promise is returned).

But with async functions this problem goes away.

Because a promise is always returned, if ANY error gets thrown within this function, it’ll get captured and a rejected promise is returned with that error. It doesn’t have to be part of the promise chain.

It’s basically giving us this for free…

That’s all for now, hope this helps you better understand async await.