Day 20: Consolidation 3
Lesson 4: Async
We're just going from big topic to big topic today aren't we?!
Asynchronous code allows us to handle code and requests that have delays without locking things up for the user or wasting time for the computer.
Things like requesting data from the internet take time, we send a request out for some data and it may take anywhere from a few hundred milliseconds to many seconds. JavaScript allows us to wrap code up in something called a promise, that will be executed in the future when the delay is over. In the mean time JavaScript can keep running other bits of code and doing other useful work.
There's lots of depth to promises and most courses will teach you how to make your own from scratch. However the vast majority of the time you will be working with promises from other libraries and other functions so rather than blow your mind or bore you to tears with an in depth triste on promises, we took the more practical approach looking at how to use and interact with promises - as 90% of the time this is what you'll be doing.
The most common place you will come across a promise is requesting data from a URL, like the Pokemon API.
fetch()
returns a promise, we can access the result of the fetch in the .then()
function when ever the response is ready. It could be less than a second or it could be more than a minute, in the meantime the rest of our JavaScript is able to keep doing other things we need without a problem.
Now remember that fetch()
is a bit of an awkward sausage and the data it returns inside that first then
is accessed with another promise res.json()
So to access the data from the PokeAPI we need to call return the res.json()
promise and then access the result of that with another .then()
which gives us the actual data.
This is called promised chaining and allows us to chain together multiple asynchronous calls, giving us confidence that they will be called in the correct order.
Errors
The most common place to handle errors in JavaScript is with network requests, there are a multitude of reasons why a network request may throw an error and we can handle them with the promise version of catch
.
It doesn't matter how many .then
s there are, one catch
at the end is enough to handle an error gracefully.
Finally
Just like before we have the promise form of finally
to run any clean up code at the end regardless of if the promise chain ended successfully or via the catch.
Async/Await
All of these .then()
s and .catch()
s with brackets and dot chaining can make code hard to read. It would be nice if we could write our asynchronous code like normal code, one line after another without having to jump in and our of all these blocks.
Well there is some JavaScript syntactic sugar called async/await
that lets us do just that.
Here is the promise based call to 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 });
And here is the async/await
equivalent.
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}
Where await
is basically unwrapping the returned promise for us and putting whatever the promise returns into the variable response
, and then the same for data
on the next line.
If we return
in either the try
or catch
block the code in finally
will still be run (if there is a finally block), don't worry about that.