How to Optimize Performance with Promises in JavaScript

How to Optimize Performance with Promises in JavaScript

The use of Promise.all and Promise.race in JavaScript

Shane Duggan • 2023 January 09 • 📖 7 min read

JavaScript Promises have become a popular tool for handling async code in web development, allowing developers to write cleaner and more efficient code. Two useful methods for optimizing async code with Promises are the .all and .race methods:

  • Promise.all() allows developers to execute multiple async operations concurrently and return a single Promise that resolves when all the async operations are completed.
  • Promise.race() allows developers to return the result of the first async operation that resolves, ignoring any other async operations that may still be in progress.

In this article, we will explore how to use Promise.all and Promise.race to optimize web performance and improve the efficiency of async code in JavaScript.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScript

What Are Promise.all and Promise.race Used For?

So, what are Promise.all and Promise.race used for exactly? Both methods are useful for optimizing async code in web development, allowing developers to write cleaner and more efficient code. I first stumbled across these powerful methods in my journey through game theory. However, I have since found that their applications spread far and wide.

Use case for Promise.all

Promise.all is particularly useful for executing multiple async operations concurrently and returning a single Promise that resolves when all the async operations are completed. This can be helpful in cases where multiple async operations need to be completed before moving on to the next step in the code. 

For example, if we want to retrieve data from different API endpoints before displaying it to the user, we can use Promise.all to ensure that all the data has been resolved before displaying it.

const promise1 = fetch('/api/data1')
const promise2 = fetch('/api/data2')

// Using Promise.all
Promise.all([promise1, promise2]).then(data => {
    // All data has been resolved
    console.log(data)
}).catch(error => {
    // An error has occurred
    console.error(error)
})
Using Promise.all
Copied to clipboard!

Use case for Promise.race

Promise.race is useful for returning the result of the first async operation that resolves, ignoring any other async operations that may still be in progress. This can be helpful in cases where we want to return the fastest response, or where we only need the result of one async operation. 

For example, if we want to retrieve data from different API endpoints and display the first response that is received, we can use Promise.race to return the fastest response. A simple example of this method in the context of fetching from an API is shown below:

const promise1 = fetch('/api/data1')
const promise2 = fetch('/api/data2')

Promise.race([promise1, promise2]).then(data => {
    // The first data to be resolved will be returned
    console.log(data)
}).catch(error => {
    // An error has occurred
    console.error(error)
})
Using Promise.race
Copied to clipboard!

In the following section, we will take a closer look at how to use Promise.all and Promise.race for optimizing performance, including code examples and best practices for async code using these methods.


How To Use Promise.all and Promise.race to Optimize Performance

Both methods have different use cases and areas to be used in to reap the rewards of web optimization. Make sure to notice the subtle differences. While both execute multiple async operations,

  • Promise.all: Returns a single Promise when all async operations are completed
  • Promise.race: Returns the fastest resolving operation from many

Let’s look at each of these methods in detail and see how and when to apply them to improve performance.

// The value of `result` will be [promise1, promise2, promise3]
Promise.all([promise1, promise2, promise3]).then(result => { ... })

// The value of `result` will be the fastest promise (one value from three)
Promise.race([promise1, promise2, promise3]).then(result => { ... })
The difference between Promise.all and Promise.race
Copied to clipboard!

How And When To Use Promise.all

The main gist of Promise.all is to remove the need to constantly fetch different sets of data from API sequentially. Instead, the method lets you complete fetch requests concurrently.

Promise.all is a powerful tool for optimizing async code, allowing developers to execute multiple async operations concurrently and return a single Promise that resolves when all async operations are completed. This can greatly improve the performance of web applications, especially when dealing with large data sets or slow network connections. Here is an example of fetching several sets of user data without Promise.all:

getUserData().then(userData => {
    console.log(userData)
    return getUserFriends(userData.id)
}).then(friends => {
    console.log(friends)
    return getUserPhotos(userData.id)
}).then(photos => {
    console.log(photos)
}).catch(error => {
    console.error(error)
})
before.js
Copied to clipboard!

To use Promise.all, you simply pass an array of Promises as an argument and specify a callback function that will be executed when all Promises have been resolved. Here is the same example using Promise.all:

const userDataPromise = getUserData()
const friendsPromise = userDataPromise.then(userData => getUserFriends(userData.id))
const photosPromise = userDataPromise.then(userData => getUserPhotos(userData.id))

Promise.all([
    userDataPromise,
    friendsPromise,
    photosPromise
]).then(([userData, friends, photos]) => {
    // All data has been resolved
    console.log(userData, friends, photos)
}).catch(error => {
    // An error has occurred
    console.error(error)
})
after.js
Copied to clipboard!

In this example, we are using Promise.all to retrieve data for a user, their friends, and their photos concurrently. The Promise.all method returns a single Promise that resolves when all Promises in the array have resolved, allowing us to handle the data in a single then callback function. 

If any of the Promises fail to resolve, the catch callback will be executed, allowing us to handle errors in a consistent and predictable manner.

How And When To Use Promise.race

Now imagine that you are building a web application that allows users to search for flights to different destinations. The application allows users to search many flight booking websites concurrently, returning the first successful result. This way, users don't have to wait for all the searches to complete before seeing the results.

searchFlightsOnWebsite1(destination).then(results => {
    console.log(results)
}).catch(error => {
    console.error(error)
    return searchFlightsOnWebsite2(destination)
}).then(results => {
    console.log(results)
}).catch(error => {
    console.error(error)
    return searchFlightsOnWebsite3(destination)
}).then(results => {
    console.log(results)
}).catch(error => {
    console.error(error)
})
before.js
Copied to clipboard!

Promise.race is the perfect tool for this scenario, allowing you to return the first successful result from the searches. An inefficient example might search for flights on each website sequentially, rather than concurrently.

This means that users will have to wait for the first search to complete before the second search starts, and so on. This can greatly increase the search time and negatively impact the user experience.

const search1 = searchFlightsOnWebsite1(destination)
const search2 = searchFlightsOnWebsite2(destination)
const search3 = searchFlightsOnWebsite3(destination)

Promise.race([search1, search2, search3]).then(results => {
    // The first successful search has completed
    console.log(results)
}).catch(error => {
    // An error has occurred
    console.error(error)
})
after.js
Copied to clipboard!

Using Promise.race in this scenario allows us to improve the performance of our application by returning the first successful result, rather than waiting for all the searches to complete. 

This can greatly improve user experience as users don't have to wait for all the searches to complete before seeing the results.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScript

Tips For Deciding When To Use Promise.all and Promise.race

Deciding whether to use Promise.all or Promise.race can be a tough decision, as both methods have their own unique advantages and disadvantages. Here are some tips to help you decide when to use each method:

  • Consider the data you are working with
    • If you are working with multiple large data sets or slow network connections, Promise.all may be the better choice as it allows you to execute multiple async operations concurrently, improving the performance of your web application.
    • On the other hand, if you only need the first successful result, Promise.race is what you want as it returns the first async operation that resolves.
  • Think about the user experience
    • If user experience and speed are key considerations, you will want to use Promise.race as it allows you to return the first successful result, rather than waiting for all async operations to complete.
    • I would recommend building some projects using these methods to get a better understanding of how to use them. Personally, I have built several projects from my foray into Linkedin Learning where I forced myself to use these methods, which gave me a clearer understanding of when and where to use them.
  • Consider error handling
    • If you need to handle errors consistently and predictably, both Promise.all and Promise.race offer catch callback functions that allow you to handle errors in a consistent manner.
    • However, if you need to handle errors separately for each async operation, you will need to use Promise.all as it allows you to handle errors for each individual operation.
  • Think about the complexity of the code
    • While both can handle multiple async operations with a single then callback, Promise.race will only let you work with the fastest response, whereas Promise.all will let you work on each operation separately.

Ultimately, the choice between Promise.all and Promise.race will depend on your specific needs and requirements. It is up to you to make an informed decision about which method is best for your situation.


Conclusion

In conclusion, Promise.all and Promise.race are the dynamic duo of async code optimization in web development:

  • Promise.all is like a team of workers all working towards the same goal, allowing you to execute multiple async operations concurrently and return a single Promise that resolves when all the async operations are completed - talk about teamwork!
  • On the other hand, Promise.race is like a sprinter, getting to the finish line (resolving the async operation) as fast as possible.

By considering the data you are working with, the user experience, the error handling, and the complexity of the code, you can decide which method is best for your project. So go forth and conquer that async code, my friends!

Share on
  • twitter
  • facebook
JavaScript Course Dashboard

Tired of looking for tutorials?

You are not alone. Webtips has more than 400 tutorials which would take roughly 75 hours to read.

Check out our interactive course to master JavaScript in less time.

Learn More

Recommended

Ezoicreport this ad