Breaking the Myth: Promise.then() Already Runs API Calls Concurrently.


JavaScript introduced the Promise API in ES6 (2015) to simplify handling asynchronous tasks. When making multiple API calls, most developers reach for Promise.all
or Promise.allSettled
.
But there's a common misconception:
"We need
Promise.all
orPromise.allSettled
to make API calls concurrent."
That's not true. Even with just Promise.then()
, API calls already run concurrently.
In this blog, we’ll break down this concept, clear up some misunderstandings, and explore why we still need Promise.all
and Promise.allSettled
.
Calling APIs Concurrently Using .then()
Consider the following code:
// First API Call
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(data => console.log('Post Data:', data))
.catch(error => console.error('Error fetching data:', error));
// Second API Call
fetch('https://jsonplaceholder.typicode.com/posts/2')
.then(response => response.json())
.then(data => console.log('Post Data:', data))
.catch(error => console.error('Error fetching data:', error));
How This Works Internally
Both
fetch
calls start at the same time.They run asynchronously in the browser’s Web APIs.
JavaScript doesn’t block execution and moves forward.
Once a response is received, the
.then()
block executes.Each API resolves independently, meaning the responses arrive in non-deterministic order.
This proves that .then()
already executes API calls concurrently.
Why Do We Need Promise.all
or Promise.allSettled
?
If .then()
already runs API calls at the same time, why do we need Promise.all
or Promise.allSettled
? The problem is that each API call finishes at its own pace, so we can’t be sure when all responses will be ready.
But what if we need to wait for every response before moving forward? That’s where Promise.all
and Promise.allSettled
help—they ensure we get all results before taking the next step.
Key Differences
Method | Behavior |
Promise.all | Resolves only if all promises succeed; rejects if any fail. |
Promise.allSettled | Waits for all promises to settle (fulfilled or rejected) and returns results. |
Can We Make .then() Handle All Responses Together?
Yes, we can! .then()
runs API calls at the same time(concurrently), but the tricky part is waiting for all responses before moving forward. Since .then()
doesn’t do this automatically, we need a way to track when all promises are done.
Instead of handling this manually every time, why not build our own version that works like Promise.allSettled
? That way, we have full control over how we manage API responses. Let’s try it out
Writing a Custom version for Promise.allSettled
Using .then()
function allSettled(promises) {
return new Promise((resolve) => {
let results = [];
let count = 0;
if (promises.length === 0) {
resolve([]);
return;
}
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
results[index] = { status: "fulfilled", value };
})
.catch((reason) => {
results[index] = { status: "rejected", reason };
})
.finally(() => {
count++;
if (count === promises.length) {
resolve(results);
}
});
});
});
}
// Example usage with API calls
const apiCall1 = fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json());
const apiCall2 = fetch('https://jsonplaceholder.typicode.com/posts/2')
.then(response => response.json());
allSettled([apiCall1, apiCall2]).then(console.log);
How This Works
let's break it down step by step:
First, we create a new promise that will eventually resolve with the results of all API calls.
We initialize an empty array to store the results and a counter to track how many promises have settled.
If no promises are given, we immediately resolve with an empty array.
For each promise in the list, we wrap it in
Promise.resolve()
to handle both regular values and promises.If a promise resolves, we store
{ status: "fulfilled", value }
in the results array.If a promise rejects, we store
{ status: "rejected", reason }
instead.In the
.finally()
block, we increase the counter. Once all promises are settled, we resolve the main promise with the results.
This gives us behavior similar to Promise.allSettled
, but using only .then()
. Pretty cool, right? It shows that we don’t always need built-in methods to handle async operations—we just need to understand how promises work under the hood!
Final Thoughts
.then()
already executes API calls concurrently.The challenge is handling responses together when order matters.
Promise.all
helps when you need all successful responses or a single failure.Promise.allSettled
ensures every request is accounted for, regardless of success or failure.
With this knowledge, you can make better decisions on handling concurrent API calls efficiently in JavaScript!
Subscribe to my newsletter
Read articles from Jayaram directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Jayaram
Jayaram
A self taught developer who loves to discuss about tech over coffee breaks