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);
});
}
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 thein-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
}
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);
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
}
So, here we are saying we will have a promise array.
let promises = [];
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));
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);
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.
2. Making API calls in parallel, collecting all the promises in an array and waiting only at the end.
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
Top comments (1)
Hi,
This was great post. Thanks
One question here
For tackling "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" we have to call the api synchronously or any other suggestions how we can achieve this?