30 Days Of JavaScript

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.

Outline

Go Pro?

If you upgraded to pro, sign in here

  • About
  • Blog
  • Privacy
Looking to email me? You can get me on my first name at allthecode.co