When we have a lot of things going on in our code following the asynchronous code can be tricky. For this reason we have two keywords async
and await
that help us deal with this problem. These two keywords can help make asynchronous read more like synchronous code. Thus our code looks cleaner while keeping the benefits of asynchronous code.
async
functionasync
keyword doesawait
keyword doesasync
functionasync
functionasync
functionLet’s start with an example. In both cases we have a function that gets some information from a server, processes it, and returns a promise. Both functions do the exact same thing.
function getCountiesInfo(name) {
return server.getCountries().then(countries => {
return countries.find(country => { return country.name === name });
});
}
async function getCountriesInfo(name) {
const countries = await server.getCountries();
const country = countries.find(country => { return country.name === name });
return country;
}
Have you noticed the async
keyword before the function declaration? How about the await
keyword before server.getCountries()
?
In order to let javaScript engine know that we are declaring an asynchronous function we need to use the keyword async
before that function. Inside that function it is required to use await
before the functions. When we are declaring a function with the async
, it automatically returns a promise. Returning in an async
function is the same as resolving a promise, likewise, throwing an error will reject the promise.
Async
functions are just syntactical sugar for promises
. The async
keyword can also be used with any of the ways a function can be created. In other words, it is valid to use an async
function anywhere we can use a normal function.
Now let’s see some examples. It’s ok if you don’t understand them.You can come back and take a look when you have gone through more stuff.
const myAsyncFunc = async () => {
// do something asynchronously and return a promise
return result;
}
myArray.forEach(async item => {
// do something asynchronously for each item in 'myArray'
// one could also use .map here to return an array of promises to use with 'Promise.all()'
});
server.getAnimals().then(async animals => {
animals.forEach(animal => {
// do something asynchronously for each animal
});
});
The await
keyword tells javaScript to wait for an asynchronous action to finish before continuing the function. The await
keyword is used to get a value from a function where we would normally use .then()
. In other words, instead of calling .then()
after the asynchronous function, we would simply assign a variable to the result using await
. Then we can use the result in our code as we would in our synchronous code.
Handling errors in async
functions is very easy. As we saw in promises, promises have the .catch()
method for handling rejected promises. Since async functions just return a promise, we can simply call the function, and append a .catch()
method to the end.
asyncFunctionCall().catch(err => {
console.error(err)
});
There is also another way that we can handle errors in async
functions. We can use the try/catch
block just like we would inside synchronous code. Thus we can handle the error directly inside the async
function.
async function getCountriesInfo(name) {
try {
const countries = await server.getCountries();
const country = countries.find(country => { return country.name === name });
return country;
} catch (error) {
// Here you can handle the error any way you'd like
}
}
This way it can look messy, however, it is very easy to handle errors without appending .catch()
after your function calls.
Keep in mind that the way you handle errors is up to you and which method you use should be determined by how your code was written. Over time you will get more comfortable of what needs to be done.
Time for some practice!!!
Now we are going to take the Giphy API and convert the promise based code into async/await
compatible code.
Here’s a refresher of the code we are starting with:
<script>
const img = document.querySelector('img');
fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
.then(function(response) {
return response.json();
})
.then(function(response) {
img.src = response.data.images.original.url;
});
</script>
Await
does not work on the global scope. Therefore we will have to create an async
function that wraps our API call to Giphy.
<script>
const img = document.querySelector('img');
async function getCats() {
fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
.then(function(response) {
return response.json();
})
.then(function(response) {
img.src = response.data.images.original.url;
})
}
</script>
Now we have a function that is asynchronous. So we can start refactoring from using promises to using await
:
<script>
const img = document.querySelector('img');
async function getCats() {
const response = await fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'});
response.json().then(function(response) {
img.src = response.data.images.original.url;
});
}
</script>
Since response
is still the same object we have passed to the .then()
block at the start, we still need to use the .json()
method, which in turn returns a promise. Since .json()
returns a promise, we can use await
to assign the response to a variable.
<script>
const img = document.querySelector('img');
async function getCats() {
const response = await fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'});
const catData = await response.json();
img.src = catData.data.images.original.url;
}
</script>
Now in order to use this function, we need to call it with getCats()
in our code.
<script>
const img = document.querySelector('img');
async function getCats() {
const response = await fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'});
const catData = await response.json();
img.src = catData.data.images.original.url;
}
getCats();
</script>
Although this code looks a bit different after refactoring, it will behave exactly the same as in the previous topic. When it comes to cleaning up asynchronous javascript code, async/await
are very useful tools. Keep in mind that async/await
are just promises written in a different way.
Now let’s dive deeper into the understanding of async/await
.
Read this article for a solid introduction to async/await. Also this article has some good examples of it’s use.
Watch the next video for a good overview on async/await and it’s purpose, along with a special trick.
When using the async/await
pair in a loop, and especially in a loop callback function such as in foreach
or `map, things start to get more complicated. Take a long break and read this interesting and important article on JavaScript async and await in loops.
In this section you can find a lot of helpful links to other content. This is a supplemental material for you if you want to dive deeper into some concepts.