Day 13: Async
Lesson 5: Await
Two things you will learn the more you code is:
1️⃣ The more indented your code becomes the harder it is to understand.
2️⃣ The more sequential you can right your code the easier it is to understand.
Promise chaining (and it's older sibling that we have't covered here: Callback hell) can make it difficult to understand what is going on in your code.
JavaScript has some syntactic sugar that makes promises a lot easier to understand called async/await. You can't use it in every case, however you can use it more often than not and it's super easy to use.
Here's the code we have been working with for fetching data from the PokeAPI:
1const pokeApi = "https://pokeapi.co/api/v2/pokemon/ditto"; 2 3let loading = true; 4 5fetch(pokeApi) 6 .then((res) => res.json()) 7 .then((data) => { 8 console.log(data); 9 }) 10 .catch((err) => console.log(err.message)) 11 .finally(() => { 12 loading = false; 13 });
Quite a bit of indentation and setting loading
happens at different indentation levels, not ideal.
Here is the same code written with the async/await syntax;
1const pokeApi = "https://pokeapi.co/api/v2/pokemon/ditto"; 2let loading = true; 3 4try { 5 const response = await fetch(pokeApi); 6 const data = await response.json(); 7 console.log(data); 8} catch (err) { 9 console.log("There was an error: ", err.message); 10} finally { 11 loading = false; 12}
So we start off making a try/catch/finally
block. Then the calls that return promises (that we would usually put a .then()
on) get replaced with await
.
So this:
1fetch(pokeApi) 2.then((res) => res.json()) 3then((data) => { ...
Becomes this:
1const response = await fetch(pokeApi); 2const data = await response.json();
Which is a lot easier to understand. Our code is flatter and we get the benefits of promises without the indentation headache. loading
is still set to true at a different level of indentation, but it's only one level in and not two so I think it's an improvement.
Let's run it in a playground.
There are a whole load of gotchas around async/await
that I am glossing over here, to give you a brief outline of the biggest one, it's this.
If you call await
inside a function, then you need to mark that function as async
in it's declaration and then call it with await
. That will have made no sense so let's look at an example.
Let's wrap our pokemon call up in a function and then call it:
1// create a function that gets Ditto and return the data 2async function getDitto() { 3 const pokeApi = "https://pokeapi.co/api/v2/pokemon/ditto"; 4 let loading = true; 5 6 try { 7 const response = await fetch(pokeApi); 8 9 const data = await response.json(); 10 11 return data; 12 } catch (err) { 13 console.log("There was an error: ", err.message); 14 } finally { 15 loading = false; 16 } 17} 18 19// call the function and then log the result 20const ditto = await getDitto(); 21console.log(ditto);
See how I have created a function called getDitto
, put our fetch code in it, returned Ditto's data? Well I've then had to put the keyword async
in front of function
and then when I call it down the bottom I have to put await
in front of it, allowing JavaScript to know to wait for it to return before moving on.
Something you may now wonder (but it's ok if you didn't) is that we return
in the try
block so will loading
ever get set back to false
? Functions end when you return
right?
Yes but this case when the return
is reached in try
control is passed to the finally
block and that then forwards the return out of the function. Which means, if you want, you can override what is return. In the playground above you can test this by adding return 42
after the loading = true
in the finally. See how it works?
Same thing would happen if you return form the catch block.