Skip to main content

Understanding Promise.all in JavaScript

 Before promises were introduced natively in JavaScript, we used a lot of callbacks for asynchronous tasks. It’s pretty common to see callbacks being used for asynchronous tasks because a lot of developers might still think that callbacks and promises are the same, but in fact, they are not.

When promises were introduced natively in JavaScript, it was definitely a game-changer. In a lot of projects, the usage of callbacks was replaced by promises for running asynchronous tasks, and promises became the main alternative for it. Promises resemble callbacks in some ways, but with an easier to follow syntax and a better understanding of the code.

When working with promises in JavaScript, we have a lot of methods that can help us. In this article, we’re going to cover the Promise.all method.

To understand how the Promise.all method works, first, we need to understand how promises work in JavaScript.

Promises

JavaScript is single-threaded, which means that we can only run one block of code at a time. It executes code in order and must finish executing code before running the next one.

A promise represents the future result of an asynchronous operation. Promises are often used to handle asynchronous tasks in JavaScript.

A promise is an object that will return a value in the future, it can either be a resolved value, which means the promise was successful, or a rejected value, which means that an error occurred. A promise will only return a value once, which means that if a promise returns an error, it will only return it once.

A promise has three possible mutually exclusive states:

  • fulfilled  —  a promise is fulfilled if promise.then(f) will call f “as soon as possible”
  • rejected  —  a promise is rejected if promise.then(undefined, r) will call r “as soon as possible”
  • pending  —  a promise is pending if it is neither fulfilled nor rejected

Sometimes we might hear that a promise is settled. That means that this promise is either fulfilled or rejectedsettled is not a state but it’s used just for convenience.

To create a promise, we use the new keyword, and inside the Promise object, we pass a function. This function is called executor, and it takes two arguments, resolve for success and reject for error:

const firstPromise = new Promise((resolve, reject) => { 
  ... 
});

Inside the promise, there’s a condition and this is where you put your logic. In case the condition is met, we use the resolve argument to return success for us. In case there’s an error, the reject argument will return an error for the promise:

const firstPromise = new Promise((resolve, reject) => {
  const sum = () => 1 + 1;
  if (sum() === 2) resolve("Success");
  else reject("Error");
});

Chaining

Promise chaining is one of the things that makes promises so great and easy to use. We can execute a chain of asynchronous tasks, each task will be executed as soon as the previous task was completed.

We can chain our promise using a .then block, anything returned from this block becomes a resolved promise:

const firstPromise = new Promise((resolve, reject) => {
  const sum = () => 1 + 1;
  if (sum() === 2) resolve("Success");
  else reject("Error");
});
firstPromise
  .then(success => console.log("success: ", success));

The beauty of the .then block is that we can perform additional async actions one after another. For error handling, we can use the .catch block:

const firstPromise = new Promise((resolve, reject) => {
  const sum = () => 1 + 1;
  if (sum() === 2) resolve("Success");
  else reject("Error");
});
firstPromise
  .then(success => console.log("success: ", success))
  .catch(error => console.log("error: ", error));

You can perform asynchronous operations by using callbacks or promises. But there are differences.

If you are using callbacks to perform asynchronous operations, in some cases you might end up having too many nested functions, this is what is called callback hell. Too many nested functions can cause your code to be unreadable and unmanageable. You can solve it by using promises, with promises you can have more readable and manageable code.

Promises are a cleaner way to run asynchronous tasks. Promises provide catch mechanism, which callbacks do not have. Promises allow cleaner, better, and functional code.

Now that we covered a little bit about promises, let’s look at Promise.all.

Promise.all

The Promise.all method takes asynchronous operations to a whole new level and helps us to aggregate and perform a group of promises in JavaScript.

Promise.all is just a promise that receives an array of promises as an input. It gets resolved when all the promises get resolved or gets rejected if one of the promises gets rejected.

You accumulated a lot of promises in your code, and you want to perform all these asynchronous operations once, without having to use some strange thing for it like a for loop, for example. How can you do it?

You either have two choices here that you can use for this use case:

  1. You can perform all the promises one by one – you can run these promises one by one or chain them and process the data as soon as it is available
  2. You can perform all promises passing them as an array input to Promise.all and the method will return a value

The better solution to use in this case is to use the Promise.all method. It will perform all the promises, return a single promise, and resolve when all of the promises passed are resolved:

const allpromises = Promise.all([Promise1, Promise2, Promise3, Promise4, ...]);

Remember, the Promise.all method will only return resolve if all the promises passed in the array returns successfully. In case there’s only one promise in the array that returns rejected, the Promise.all method will return rejected.

For example, let’s imagine that we have a function called sum. This function will just return the value of some operation for us:

const sum = (a, b) => a + b;

Now, let’s imagine that we have five promises, and inside each one of these promises we’re going to use the sum function and inside an if statement, compare the value. In case it’s true, we are going to return a success message and in case it’s false we are going to return an error message:

const first = new Promise((resolve, reject) => {
  const value = sum(1, 1);
  if (value === 2) resolve(value);
  else reject(value);
});

const second = new Promise((resolve, reject) => {
  const value = sum(2, 2);
  if (value === 4) resolve(value);
  else reject(value);
});

const third = new Promise((resolve, reject) => {
  const value = sum(3, 3);
  if (value === 6) resolve(value);
  else reject(value);
});

const fourth = new Promise((resolve, reject) => {
  const value = sum(4, 4);
  if (value === 8) resolve(value);
  else reject(value);
});

const fifth = new Promise((resolve, reject) => {
  const value = sum(5, 5);
  if (value === 10) resolve(value);
  else reject(value);
});

To perform all promises at once, we pass an array input to Promise.all:

const allPromises = Promise.all([first, second, third, fourth, fifth]);

Now, we just call our single promise called allPromises and it will return to us an array of resolved values:

allpromises.then(success => console.log('sucess: ', success)).catch(error => console.log('error: ', error));
// Result
// sucess: [ 2, 4, 2, 8, 10 ]

In case one of the promises returns an error, our single promise will return an error as well. In our example, inside the fifth promise, we are going to pass as arguments for the sum function the values 5 and 6.

Of course, this will return an error as 5 + 6 is not 10. This will cause our single promise to return an error:

const fifth = new Promise((resolve, reject) => {
  const value = sum(5, 6);
  if (value === 10) resolve(value);
  else reject(value);
});

const allpromises = Promise.all([first, second, third, fourth, fifth]);
allpromises.then(success => console.log('sucess: ', success)).catch(error => console.log('error: ', error));

// Result
// error:  11

Promise.all vs. Promise.allSettled

You have many promises that you want to perform but the Promise.all might not be the best solution for you if you want to return all the values, regardless if there is an error in your promises.

You can use the Promise.allSettled method for it. This method will return a single promise that will be resolved after all the promises were either fulfilled or rejected.

Let’s use our last example, and instead of using the Promise.all method, we are going to use the Promise.allSettled method:

const allpromises = Promise.allSettled([first, second, third, fourth, fifth]);
allpromises.then(success => console.log('sucess: ', success)).catch(error => console.log('error: ', error));

// Result
// success:  [
//   { status: 'fulfilled', value: 2 },
//   { status: 'fulfilled', value: 4 },
//   { status: 'fulfilled', value: 6 },
//   { status: 'fulfilled', value: 8 },
//   { status: 'rejected', reason: 11 }
// ]

When to use

To use the Promise.all method, you need to know first what you need to achieve. The Promise.allmethod is very helpful and useful in some cases, for example:

  1. The tasks that you are performing are dependent on each other and you want to know if all of the promises have finished successfully
  2. You need to make requests to different APIs and after all the responses you want to do something with the result

The Promise.all is a great way to achieve concurrency in JavaScript, it is one of the best ways to perform concurrent asynchronous operations in JavaScript when you have multiple promises and you want to perform all of them.

Conclusion

In this article, we covered a little bit about promises in JavaScript and learned more about a promise method called Promise.all. This method is a very helpful and useful method to aggregate and perform many promises and return a single promise with all the values inside an array.

Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...