Last Updated: February 4, 2023
Async Await
What is Async/Await and how do we use them with Promises?
Ađ°Aâ
... in other words: Async Await
What is async/await
? Why does it exist? What do we use it for? And when?
In this video about Asynchronous Programming I showed you what asynchronous programming is vs. synchronous, we used callbacks to achieve async, then in episode 12 we took that callback solution and converted it to Promises.
In this episode weâll take the same example and convert it to use async/await, ALONGSIDE promises. If you havenât seen those two episodes I suggest you do that first. Here they are. Then Iâll show you a couple more realistic and interesting examples and weâll go from there.
If you remember from the Promise Video there are two parts to any promise. Thereâs the promise creator, and the promise receiver. In other words, you have one piece of code that creates a promise and returns it, like this function:
function getData() { return new Promise(function(resolve, reject) { resolve('some data'); }); }
then you have another piece of code that calls the function, takes the promise and handles it using a then block, something like this:
const promise = getData() promise.then(function(data) { const result = data })
We can actually get rid of the promise variable and write it like this
getData().then(function(data) { const result = data });
So take a look at that result
variable. Itâs inside a then
block. That kind of annoys me. I donât like that itâs inside a separate block. But we need to do that because this is async code.
But wouldnât it be cool if we could write async code as if it was synchronous? Like regular serial code reading top to bottom, one line after another? Instead of nesting code inside code blocks?
Like something like this where we just create our result variable and assign it to the return value of the getData
function:
const result = getData()
Well, no because getData
doesnât return the actual value, it returns the promise of a value. I explain why in full detail in this video. Or in 1 minute in this YouTube Short about Promises.
The bottom line is JavaScript thinks this is synchronous code and doesnât wait for the functionâs async operation. Unless...!
You put await
in front of the function invocation.
const result = await getData();
Are you serious?? Letâs find out, CRAIG!
Letâs create a promise:
function getData() { return new Promise((resolve) => { setTimeout(() => { resolve(46) }) }) }
Letâs call it and see what we get
const result = getData() console.log(result) // Promise
Ok, itâs obviously just a Promise
. Now letâs use await
and call it:
const result = await getData() console.log(result) // SyntaxError
What? Error!
SyntaxError: await is only valid in async functions and the top level bodies of modules
So whatâs happening here? The reason why itâs called async/await
is because in order to use await
you need to use async
, in other words await
can only be used inside functions that are async and since weâre not inside an async
function we canât use await
. Actually weâre not in a function at all. So how do we make an async
function? Simple, you create a function and put async
in front of it:
async function start() { const result = await getData() console.log(result) // ? }
So now we actually have to call our function, which is annoying but stay with me for a second:
start() // 46
Ok so hereâs the magic: I can call my getData
function as if it was synchronous! Itâs the difference between this:
const result = await getData()
and this:
getData().then(data => { const result = data })
So I can start using the result of my async operation as a regular variable, as opposed to some internal variable declared inside a then block. I like that.
Letâs simplify a bit. Letâs get rid of getData
function, where we faked a timeout, letâs use a more realistic example. Letâs use the browserâs native fetch
API to call an endpoint and get some data. We did this in the video about Promise too.
We know fetch
returns a promise. So instead of creating this middleman function called getData
letâs just call fetch
.
async function start() { const data = await fetch('https://api.weather.gov/gridpoints/OKX/35,35/forecast'); const weather = await data.json() console.log(data.properties.periods[1].shortForecast) } start();
compared to:
function start2() { const result = await fetch('https://api.weather.gov/gridpoints/OKX/35,35/forecast') .then(data => result.json()) .the(data => data.properties.periods[1].shortForecast) }
A few important points:
1. Async and Await have to be used together
Like you saw we can only use await inside the body of an async function. Otherwise you get a syntax error:
####Two exceptions:
await
can be used on its own with JavaScript modules. I donât wanna encourage or discourage it. Thereâs a link below, you can read more about it on MDN.- Chrome DevTools allows you to use
await
, as of Chrome 62, so for a long time. So if youâre testing things out inside chrome devTools which I do all the time, and you donât get a syntax error thatâs why.
2. Async Await only affects the promise receiver
When using async/await
you donât change the promise creation. That stays the same. It only effects the promise usage side, meaning where you get returned a promise.
3. You can put await
in front any function...
...that returns a promise. Like you saw we did it with getData
but also with fetch
.
4. Any function can become async
Can I put async
in front of other function? Sure you can, Qoli!!!
Hereâs an object method that returns a string. I can just change it to async and BOOM, itâs now async.
const me = { async sayHello() { return 'I am Qoli' // `I am Qoli` } } me.sayHello() // Promise { `I am Qoli` }
This means I can perform async operations inside the function and await the result. I donât â˘have* to do anything async, but if I did I could write it with await
, like this.
async function sayHello() { const status = await getMyStatus() return status }
You might have noticed the next point:
5. Async functions all return promises automatically
If you convert a function to async
, it will make your function return a promise by default, even if you explicitly return something else. Check out the Qoli
example above again âď¸
It sure looks like it returns a string
but it doesnât, it returns a promise
, a resolved promise
. Hereâs an even simpler example:
async function myFun() { return 1 // gets converted to return Promise.resolve(1) }
6. Error handling with try/catch
One of the most important, yet neglected JavaScript skills is error handling.
In the Promises video I showed you how to handle errors with the catch
block. We spent almost half of that video talking about error handling.
So if we donât have the then
block with async await
then we probably donât have the catch
block either, right? Well, kind of...
You can use a catch
block with await
but you can also catch errors using a more old school method called try/catch
. Here is how:
function getData() { return new Promise(function(resolve, reject) { setTimeout(() => { reject('Something went wrong!') }, 1) }) } async function start() { try { const result = await getData() } catch (error) { console.log(`Oops: ${error}`); } } start()
Now, with that said, you can still use a catch block:
async function start() { const result = await getData().catch(e => {console.log(e)}) console.log(result) }
The issue is your console.log
will still run as if there was no error, this is a problem.
For one thing, we donât want any of the success logic to run if the promise was rejected, so thatâs potentially creating bugs.
But also we need to be able to separate the successful and failed scenarios in our code for readability and organization purposes.
And lastly, whatâs the point of writing async await
if youâre not going to take advantage of the synchronous looking nature of it? So thatâs why it's pretty typical to use try/catch
instead of a .catch
block.
Now, are you gonna be able find some assh*le who can find a case against what I just said? Sure... But I donât recommend it.
Ok, that about covers the basics of async await
. Now go try it! Ok bye.