DEV Community

loading...

javascript: Making multiple API calls the right way

Ravi Ojha
・3 min read

Recently while reviewing a PR I realized a function was making multiple calls one by one which could have been making calls in parallel. In this post I want to share two different pieces of code (with mock) and a graph which shows how both of them progresses independently.

Let's start!!

Suppose there is a scenario where we have to make multiple calls to an API to get the full list of users, suppose there are 500 users in system, but API is capped by maximum pageSize, which could be let's suppose 100.

With above assumptions we will have to make 10 calls to get the full list of users.

Let's create a mock/fake API endpoint:

function fakeApiCall(currentPage) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        data: {
          currentPage,
          total: 100000,
        },
      });
    }, 1000);
  });
}
Enter fullscreen mode Exit fullscreen mode

This returns a promise which will be resolved after 1000ms

One more use case: Let's suppose we get a requirement to get the list of all users to process something beforehand (it could be anything, for eg: may be we just want to show the list of active users when admin lands on Admin Panel, to do that we will have to get all the users and then filter out the in-active one).

1. First way to solve this:

async function oneByOneCall() {
  ...
  let currentPage = 1;

  while (currentPage <= 5) {
    let { data } = await fakeApiCall(currentPage);
    result = [...result, data];
    currentPage++;
  }
  ...
  // Do something with final result
}
Enter fullscreen mode Exit fullscreen mode

async/await brings a lot of comfortability while dealing with promises, it's also very easy to write and understand code.

But, let's look at the code one more time.

So, what is happening here.

let { data } = await fakeApiCall(currentPage);
Enter fullscreen mode Exit fullscreen mode

Here we are saying, make the api call and wait for the result to come back, then process that result and then continue with while loop. Execution will kind of stop at this point while waiting for result to come back

huh?? That's not what we want, we wanted to process it in parallel. right?

NOTE: There will be scenarios where you will have wait, could be because you are making calls to multiple APIs and you want to decide or send some data from previous response, but thats not something we are tackling here

2. Second way to solve this:

async function parallelCall() {
  ...
  let start_time = new Date().getTime();
  let promises = [];
  let result = [];

  let currentPage = 1;

  while (currentPage <= 5) {
    promises.push(fakeApiCall(currentPage));
    currentPage++;
  }

  const data = await Promise.all(promises);
  data.forEach(({ data }) => {
    result = [...result, data];
  });

  ...
  // Do something with final result
}
Enter fullscreen mode Exit fullscreen mode

So, here we are saying we will have a promise array.

  let promises = [];
Enter fullscreen mode Exit fullscreen mode

And in loop we are going to push each promise returned from fakeApiCall and not going to wait for result right now.

    promises.push(fakeApiCall(currentPage));
Enter fullscreen mode Exit fullscreen mode

When we are done with loop or collecting all the promises, we are going to wait for each of them to be finished only once.

  const data = await Promise.all(promises);
Enter fullscreen mode Exit fullscreen mode

And here is the graph for better representation of the execution sequence and their comparison:

1. Making API calls one by one, waiting for response after every call.

image

2. Making API calls in parallel, collecting all the promises in an array and waiting only at the end.

image

So, finally, we can see we are gaining close to 4 secs (4000ms) here. That's a huge gain. So second option is much better than going with first option.

The code used in this post is available here.

-- Thanks, Ravi

Discussion (0)