Skip to content

Efficiently Managing Multiple HTTP Requests in JavaScript

Published: at 10:00 AM

Imagine a webpage that needs to fetch data from three different sources: Categories, Authors, and Posts. Since this data will be retrieved via API calls, the response times will be unpredictable. If these datasets are independent of each other, we can fetch them as follows:

getCategories().then(categories => setCategories(categories))
getAuthors().then(authors => setAuthors(authors))
getPosts().then(posts => setPosts(posts))

/* At this point, we don't have any data for Categories, Authors, or Posts yet. */

JavaScript executes these API calls concurrently, without waiting for each response. This approach works well when the datasets are independent. However, what if we need all three datasets to be available before we can process them? Let’s examine a common but suboptimal approach:

const categories = await getCategories()
const authors = await getAuthors()
const posts = await getPosts()

/* At this point, we now have the data for Categories, Authors, and Posts. */

While this code appears straightforward, it’s not the most efficient solution. Let’s analyze why by looking at some example response times:

Behind the scenes, this code actually works like this:

getCategories().then(categories => {
    getAuthors().then(authors => {
        getPosts().then(posts => {
            /* At this point, we now have the data for Categories, Authors, and Posts. */
        })
    })
})

This creates a sequential chain where each request waits for the previous one to complete. With our example timings, we end up waiting 4 seconds total (1.4 + 1.0 + 1.6) to receive all the data.

Request timing comparison

A more efficient approach is to start all requests simultaneously while tracking when they all complete. JavaScript provides this functionality through Promise.all(). Here’s how to implement it:

const [categories, authors, posts] = await Promise.all([
  getCategories(),
  getAuthors(),
  getPosts()
])

/* At this point, we have all three datasets available simultaneously */

The key advantages of this approach are:

  1. All requests start simultaneously
  2. The results maintain their order in the returned array
  3. We can use array destructuring for cleaner code

With this method, our total waiting time becomes 1.6 seconds - only the duration of the longest request. This represents a 60% reduction in loading time compared to the sequential approach, significantly improving the user experience.

Concurrent request timing comparison

When using Promise.all(), it’s essential to consider error handling. If one or more promises reject, Promise.all() will immediately reject with the reason of the first promise that rejected. You can handle this by wrapping your code in a try-catch block or using the .catch() method. Additionally, if you want to allow other promises to continue executing even if one is rejected, you can use Promise.allSettled() instead.

By adopting this concurrent approach with Promise.all(), you can significantly improve the performance and responsiveness of your web applications, leading to a better user experience.