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
thatresolves
, it will convert thepromise
resolving where you would normally use.then()
to access the returnedvalue
.
similar to…
Using await
allows a much nicer flatter structure that just looks like synchronous code.
- When used on a
promise
thatrejects
, it will convert thatpromise
rejection
into a thrown thaterror
becomes…
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 apromise
thatresolves
with thatvalue
you returned. - If inside an
async
function an error is thrown, externally that will be as if the function returned apromise
thatrejects
with thaterror
.
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
.