Welcome back! Excited to have you in this journey with me!
There are 4 parts in this series:
- Intro to Asynchronous JS
Promises
- More
Promises
(this article) async/await
Introduction
In the last article, we covered the basics of Promises. Now we are learning something a little bit more advanced!
Here's a quick recap from the last article, we learned:
- The different states of a promise: pending, resolved, rejected
- The Consumers in
Promises
:.then
,.catch
,.finally
With the knowledge above, in this article, we would cover chaining promises and fetching multiple promises.
Chaining Promises + Real Example
Now that we learned about handler methods, it is time to put them in a more practical example. We usually see multiple .then
in a promise as we want to do more things. Do you still remember one of the asynchronous callbacks: network request from Intro to Asynchronous JavaScript (or if you didn't read the article, that's fine too!)?
We are going to use that and make it a promise and we'll use our handlers in this example. Fetching data/third-party API is pretty common and we usually fetch data asynchronously.
We will use Fetch API, which is registered with the fetch()
method. You may wonder why fetch()
. fetch()
not only is very similar to our old friend XMLHttpRequest
, but also starts a request and returns a promise. Therefore, you'll see fetch()
in a lot of articles related to asynchronous programming in JS, including this one.
Without further ado, let's see how it works!
Note: If you want to code along, please create a new HTML file, add a <div id="main"></div>
and attach <script>
at the bottom of the HTML <body>
!
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Characters from Harry Potter</title>
</head>
<body>
<h1>Characters from Harry Potter</h1>
<div id="main"></div>
<script>
</script>
</body>
</html>
Step 1: Check out the HTTP request first.
// fetch the third-party API
const fetchHP = fetch("http://hp-api.herokuapp.com/api/characters")
console.log(fetchHP);
As we can see above, it is not returning above but a pending promise. As we are making an HTTP request as an asynchronous operation, fetch will not return any data.
Step 2: Next, we'll use the .then
method to attach a callback once our promise is fulfilled!
// fetch the third-party API
const fetchHP = fetch("http://hp-api.herokuapp.com/api/characters")
fetchHP.then(response => console.log(response));
Step 3: Knowing that the response works (i.e. the promise is fulfilled), we want to return this response in json()
method. As json()
is also a promise, we would need to create a promise chain for this:
// fetch the third-party API
const fetchHP = fetch("http://hp-api.herokuapp.com/api/characters")
fetchHP.then(response => {
return response.json();
}).then(characters => console.log(characters));
After you refresh your browser and when you check your console, now it returns all the characters from the API:
Step 4: Now that we have all the characters, I will create another function to map the character's name one by one with another and to print all the names onto our web page:
// fetch the third-party API
const fetchHP = fetch("http://hp-api.herokuapp.com/api/characters")
// grab that main element
const main = document.getElementById("main");
fetchHP.then(response => {
return response.json();
})
.then(characters => {
main.innerHTML = mapCharacters(characters);
});
const mapCharacters = (characters) => {
const names = characters.map(character => `<li>${character.name}</li>`);
return `<ul>${names}</ul>`
}
With all that combined, you should see this (please ignore the ","):
Last but not least, let's go ahead and add .catch
handler, in case any errors with our promise:
// fetch the third-party API
const fetchHP = fetch("http://hp-api.herokuapp.com/api/characters")
// grab that main element
const main = document.getElementById("main");
fetchHP.then(response => {
return response.json();
})
.then(characters => {
main.innerHTML = mapCharacters(characters);
});
// HERE: error handler
.catch(error => {
console.log(error)
});
const mapCharacters = (characters) => {
const names = characters.map(character => `<li>${character.name}</li>`);
return `<ul>${names}</ul>`
}
There you go! You got all your Harry Potter characters on your web app. You can play around by adding images, houses, etc.!
ππ»ββοΈ Author's note:
- As you may have noticed, I have been using GIFs from the MCU throughout the series. As much as I would love to use Marvel API as demonstration, however, they don't have a public API and requires an API key. Therefore, we'll be using Harry Potter API instead.
- If you are still interested in using the Marvel API, here's the documentation.
- As of June 20, 2021, the Harry Potter API I initially would like to use is currently under maintenance. Hopefully by the time when you read this article, they are available for use. There may be some difference with the key-pairs in the API.
Now that we learned about chaining promises, let's switch the gears a bit! There is a possibility that we have to fetch multiple promises. Let's see how it's done in the next section.
Promise.all
There are 6 static methods in the Promise
class, including: Promise.all
, Promise.allSettled
, Promise.race
, Promise.any
, Promise.resolve(value)
, Promise.reject(error)
.
Promise.all
is the most common in practice, therefore I will only cover this in this article.
Q: When would we use Promise.all
?
A: When we have multiple promises to execute in parallel. Examples like fetching multiple URLs in parallel and process the content.
Promise.all
takes an array of promises and returns a new promise. Let's how it looks like with setTimeOut()
:
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 2000)),
new Promise(resolve => setTimeout(() => resolve(2), 2000)),
new Promise(resolve => setTimeout(() => resolve(3), 2000)),
]).then(alert);
(Code reference from javascript.info)
Quick demo:
Let's include error handler and finally handler and see how it looks like:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve(console.log(1)), 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(console.log(2)), 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve(console.log(3)), 3000);
});
const p4 = new Promise((resolve, reject) => {
setTimeout(() => resolve(console.log(4)), 4000);
});
const p5 = new Promise((resolve, reject) => {
reject(new Error('reject'));
});
Promise.all([p1, p2, p3, p4, p5])
.then(values => console.log(values))
.catch(error => console.log(error))
.finally(() => console.log("Promise done!"));
If you are interested in seeing what it is like fetching multiple URLs, please check out here. They use an example of fetching multiple Github profile and print the user names on the alert.
Also allow me to give you a reminder -- if you are a semicolon person, make sure to put the semicolon after you are done with ALL the handlers, there's no need to put the semicolon at the end of every handler.
There you go! This is more like a high level of slightly advanced Promise knowledge, but hopefully you get the gist of what Promise chaining is like, as well as fetching promises!
Next up, we'll discuss the newer asynchronous operator: async
/await
!
To make up the lack of the MCU GIF in this article...
Resources
π How to make HTTP requests using Fetch API and Promises (Medium blog by Armando Amador)
π Graceful asynchronous programming with Promises: Chaining the blocks together (MDN)
π Promise.all() (MDN)
π Promise API (javascript.info)
Top comments (0)