DEV Community

Megan Lo
Megan Lo

Posted on

More Promises in JavaScript

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

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 (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>
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Promise Pending

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));
Enter fullscreen mode Exit fullscreen mode

HP 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));
Enter fullscreen mode Exit fullscreen mode

After you refresh your browser and when you check your console, now it returns all the characters from the API:
HP Characters

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>`
}
Enter fullscreen mode Exit fullscreen mode

With all that combined, you should see this (please ignore the ","):
Final Result

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>`
}
Enter fullscreen mode Exit fullscreen mode

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.

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():

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);
Enter fullscreen mode Exit fullscreen mode

(Code reference from javascript.info)

Quick demo:

Promise.all: setTimeout()

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!"));
Enter fullscreen mode Exit fullscreen mode

Quick demo:
Promise.all with error handler

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...
bye bye GIF


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)