More Promises in JavaScript
Tags: javascript
Published: 2021-06-20 19:42:53

Welcome back! Excited to have you in this journey with me!

<img src="https://media4.giphy.com/media/EbRPam1A4jEWkUokL8/giphy.gif" alt="Welcome Back GIF" />

There are 4 parts in this series:
1. Intro to Asynchronous JS 
2. `Promises`
3. More `Promises` (this article)
4. `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](https://dev.to/mehmehmehlol/intro-to-asynchronous-javascript-g9e) (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>`!


```javascript
<!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.
```javascript
// fetch the third-party API
const fetchHP = fetch("http://hp-api.herokuapp.com/api/characters")

console.log(fetchHP);
```

![Promise Pending](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njt5xl0auikmg6305cx6.png)

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!
```javascript
// fetch the third-party API
const fetchHP = fetch("http://hp-api.herokuapp.com/api/characters")

fetchHP.then(response => console.log(response));
```

![HP Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al8o8bzizbj7ej0tih2f.png)
 
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:
```javascript
// 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: 
![HP Characters](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rx462o28h5yy3ppr0u5p.png)

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:
```javascript
// 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 ","):
![Final Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b4ja1wyi8m36x15nwq3o.png)
 
Last but not least, let's go ahead and add `.catch` handler, in case any errors with our promise:
```javascript
// 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](https://developer.marvel.com/documentation/getting_started).
- As of June 20, 2021, the [Harry Potter API](https://www.potterapi.com/) 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.

<img src="https://media.giphy.com/media/10PptCqDkZIkZW/giphy.gif" alt="Snap approves GIF" />

---

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()`:
```javascript
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](https://javascript.info/promise-api))

Quick demo: 

![Promise.all: setTimeout()](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4at8rzoln4esrjbg4y8.gif)
 
Let's include error handler and finally handler and see how it looks like:
```javascript
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!"));
```

Quick demo: 
![Promise.all with error handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8i0usoc9pptbt1gadyw.gif)

If you are interested in seeing what it is like fetching multiple URLs, please check out [here](https://javascript.info/promise-api#promise-all). 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...
<img src="https://media1.tenor.com/images/08951203bc99246259bfe508b71f2e3b/tenor.gif?itemid=13519149" alt="bye bye GIF" />

---

## Resources
🌟 [How to make HTTP requests using Fetch API and Promises](https://medium.com/@armando_amador/how-to-make-http-requests-using-fetch-api-and-promises-b0ca7370a444) (Medium blog by Armando Amador)
🌟 [Graceful asynchronous programming with Promises: Chaining the blocks together](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises#chaining_the_blocks_together) (MDN)
🌟 [Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) (MDN)
🌟 [Promise API](https://javascript.info/promise-api) (javascript.info)