Asynchronous programming in JavaScript allows resource-intensive operations to run in the background without interrupting the main thread. Operations like API calls and file processes are some of the operations which should be run asynchronously.
Promises. all() is a powerful function that can manage these operations concurrently. This article will cover how to manage multiple promises concurrently with Promise.all()
Let’s dive in.
A promise is an object that represents the eventual failure or completion of an asynchronous event. Let’s look at a simple promise.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); });
A promise takes a function with two parameters, resolve and reject. In our example, the promise will resolve if the operation is successful (i.e., if the userId===1.If the operation fails the promise will be rejected.
The lifecycle of a promise starts in the pending state, and it will eventually either be fulfilled or rejected. Currently, the promise is pending. To consume the promise, we call .then() to handle the result.
The output will either be the user data(if fulfilled) or an error(if rejected).
promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
Since the operation is successful, the promise will resolve.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); }); promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
if we change the value of userId, the promise will be rejected and you will get the error User not found
Suppose you had multiple promises, you would handle each promise independently like this:
const promise1 = new Promise((resolve, reject) => resolve(1)); const promise2 = new Promise((resolve, reject) => resolve(2)); const promise3 = new Promise((resolve, reject) => resolve(3)); promise1 .then((value) => { console.log(value); promise2 .then((value) => { console.log(value); promise3 .then((value) => { console.log(value); }) .catch((err) => { console.log("promise3 error", err); }); }) .catch((err) => { console.log("promise2 error", err); }); }) .catch((err) => { console.log("promise1 error", err); });
There are a few potential issues that will arise from the execution above:
Each promise runs after the previous one is completed. promise2 will start after promise1 resolves and promise3 will start after promise2 resolves; This slows down execution.
The nested structure in the .then chaining results in “callback hell”, making the code harder to read and maintain.
Each error is handled independently which adds to more complexity.
A better approach would be to use Promise.all(),which allows promises to run at the same time, hence improving performance and error handling
Promise.all() takes an iterable of promises and returns a single promise. The syntax looks like this:
Promise.all(iterable)
If we use Promise.all() In our earlier example, we have something like this:
Promise.all([promise1, promise2, promise3]) .then((values) => { console.log(values); }) .catch((err) => { console.log("promise all error", err); });
As you can see, this approach is cleaner and easier to understand.
JavaScript is a single-threaded language, meaning that each piece of code waits for the previous one to complete before going to the next.
So if JavaScript is single-threaded, how does Promise.all()handle multiple promises?
Promise.all()operates on the concurrency principle, which means that all the promises will start executing not necessarily at the same moment, but initiated without waiting for one to complete before starting the next.
Promise.all()only resolves when all the promises in the iterable are fulfilled, However, if any of the promises in the iterable rejects, Promise.all() will reject immediately and ignore the result of the remaining promises.
Promise.all() excels in scenarios where you need to perform multiple independent asynchronous operations and wait for all of them to finish before proceeding.
Let’s look at some of these examples where Promise.all() can be used to improve efficiency in real-world applications.
Consider a scenario where you are working on an application that fetches data from two different APIs simultaneously.
Let’s attempt to fetch the data sequentially from multiple API’s and also log the time taken to finish the request.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); });
Here is the output:
The time taken to process the request is 50.36 ms. This execution time can be improved. To illustrate the benefits of concurrency, Let’s compare the approach using Promise.all()
promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
Here we are using Promise.all() to run multiple asynchronous operations concurrently. Promise.all()will take an array of promises and return a single promise when all the promises have resolved.
Here is the output.
From the output, we can see that using Promise.all() is slightly more efficient: This improvement occurs because Promise.all()allows both operations to start simultaneously, rather than waiting for one to finish before starting the other.
In real-world applications with more complex operations or additional API calls, the performance gains from using Promise.all()can be even more significant.
However, if you want to wait for all the promises to settle, regardless of whether they fulfill or reject, you can use Promise.allSettled()
In this case, all the data needs to be sent at the same time. In this case, you can use Promise.all() and send all the requests concurrently, and then wait for all of them to resolve before getting the results.
For example, suppose we needed to analyse this sample data:
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); });
In this case, all the data needs to be sent at once; sending data sequentially will be time-consuming. Instead, we will use Promise.all() to initiate multiple API calls simultaneously.
You will have something like this:
promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
Suppose you have an application that accepts bulk uploads from users. After taking all the necessary measures to validate the files, you can use Promise.all()to perform multiple file reads in parallel. This is far more efficient than reading each file one by one in a sequential manner.
Without Promise.all(), you would have to wait for each file to be read completely before reading the next file. This would lead to more processing time, especially if you have a larger number of files.
However, with Promise.all(), all file reading operations are initiated simultaneously, leading to considerable time savings and a great user experience.
const userId = 1; let promise = new Promise((resolve, reject) => { setTimeout(() => { if (userId === 1) { resolve({ name: "John Doe", email: "john@example.com" }); } else { reject(new Error("User not found")); } }, 1000); }); promise .then((data) => { console.log(data); }) .catch((err) => { console.log(err); });
It’s also important to note that when reading many large files simultaneously, you should be mindful of potential memory considerations.
In conclusion, Promise.all() offers a lot of benefits which are summarised below
Cleaner Code: Promise.all()makes your code easier to understand since you don’t have nested .then() chains. Requests are handled in a single .then() block.
Efficient: By making requests concurrently, your application’s overall performance improves, as the total time required to fetch the data is reduced.
Get practical JavaScript tips and code snippets delivered to your inbox. Join 1000 developers who write better code.
The above is the detailed content of How to Manage Multiple promises concurrently with Promise.all(). For more information, please follow other related articles on the PHP Chinese website!