codewithjohn.dev
Published on

Mastering JavaScript Promise.all()

In this blog post, we will explore how Promise.all() works in depth, and understand its syntax, and dive into real-world code examples to understand its potential.

Table of Contents

Understanding Promises

Before diving into Promise.all(), it's essential to understand JavaScript Promises. In JavaScript promises are way for handling asynchronous operations.

A promise can exist in one of three states

  • Pending
  • Fulfilled
  • Rejected

Initially, a promise is in the pending state, indicating that the process is still ongoing and not yet completed. If the operation is successful, the promise transitions to the fulfilled state. However, if an error occurs during the process, the promise transitions to the rejected state.

Imagine you have a website that displays random images of animals. You want to fetch a new image from an API and display it on your website. You can use a promise to handle this asynchronous action.

Here's an example:

const fetchImage = new Promise((resolve, reject) => {
  // Simulate fetching an image from an API
  setTimeout(() => {
    const imageURL = 'https://example.com/animal.jpg';
    const error = false; // Set to true to simulate an error

    if (!error) {
      resolve(imageURL); // Promise is fulfilled
    } else {
      reject('Error fetching image'); // Promise is rejected
    }
  }, 2000); // Simulate 2 seconds delay
});

fetchImage
  .then((imageURL) => {
    // Display the image on your website
    const imageElement = document.createElement('img');
    imageElement.src = imageURL;
    document.body.appendChild(imageElement);
  })
  .catch((error) => {
    // Handle the error
    console.error(error);
  });

Introducing Promise.all

In JavaScript Promise.all is a method that is used for handling asynchronous operations. Promise.all that takes an array of promises as an input (an iterable) and returns a single Promise. This returned promise will fulfill when all the input promises have fulfilled, or reject immediately if any of the input promises reject.

Syntax and Usage

The syntax for Promise.all is straightforward:


Promise.all(iterable);

Here, the iterable is an array or any other iterable object containing promises.

Handling Rejections

By default, Promise.all will reject as soon as any of the input promises reject.

Here's a simple example to demonstrate the rejection of Promise.all in JavaScript:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 1 resolved');
  }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('Promise 2 rejected');
  }, 1000);
});

Promise.all([promise1, promise2])
  .then((results) => {
    console.log('All promises resolved:', results);
  })
  .catch((error) => {
    console.log('At least one promise rejected:', error);
  });

In the above example, we have two promises: promise1 and promise2. promise1 resolves after 2 seconds, while promise2 rejects after 1 second. Since promise2 rejects after 1 second the catch block is executed, and we get the error from the first rejected promise.

Non-Promise Values:

Promise.all() is not limited to Promises only; it can also handle non-Promise values. Any non-Promise values in the input array are immediately resolved:

const promise1 = new Promise((resolve) => setTimeout(resolve, 1000, 'One'))
const promise2 = 'Two'
const promise3 = new Promise((resolve) => setTimeout(resolve, 3000, 'Three'))

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values) // Output: ["One", "Two", "Three"]
  })
  .catch((error) => {
    console.error(error) // This will not be executed in this example
  })

Real-World Examples with Code

Fetching multiple user profiles from an API

const userIds = [1, 2, 3]; // Array of user IDs to fetch profiles for

const fetchUserProfile = (userId) => {
  return fetch(`https://api.example.com/users/${userId}`)
    .then(response => response.json())
    .then(profile => {
      console.log(`Fetched profile for User ID ${userId}:`, profile);
      return profile;
    });
};

Promise.all(userIds.map(fetchUserProfile))
  .then(profiles => {
    console.log('All profiles fetched:', profiles);
    // Do something with the fetched profiles
  })
  .catch(error => {
    console.error('Error fetching profiles:', error);
  });

Uploading multiple images to a cloud storage service:

const imageFiles = [file1, file2, file3]; // Array of image files to upload

const uploadImage = (file) => {
  return new Promise((resolve, reject) => {
    const storageRef = firebase.storage().ref();
    const imageRef = storageRef.child(`images/${file.name}`);

    imageRef.put(file)
      .then(snapshot => {
        console.log(`Uploaded ${file.name}`);
        resolve(snapshot);
      })
      .catch(error => {
        console.error(`Error uploading ${file.name}:`, error);
        reject(error);
      });
  });
};

Promise.all(imageFiles.map(uploadImage))
  .then(snapshots => {
    console.log('All images uploaded:', snapshots);
    // Do something with the uploaded image snapshots
  })
  .catch(error => {
    console.error('Error uploading images:', error);
  });

Conclusion

Promise.all([...]) is a useful helper function that lets you to handle asynchronous operations Promise.all([...]) takes an array of promises as an input (an iterable) and returns a single Promise. After reading this article, I hope you feel confident about using Promise.all([...]) . Happy coding!

Like what you are reading?