2Minutes: Parallel Requests In JavaScript
What are Parallel Requests in JavaScript?
We often write code similar to this when we need data from more than one source:
async function getUserAndPosts(userId) {
const user = await fetch(`https://api.example.com/users/${userId}`).then(res => res.json());
const posts = await fetch(`https://api.example.com/users/${userId}/posts`).then(res => res.json());
return { user, posts };
}This works fine, but it’s slower than it needs to be. Because await pauses the function until the Promise in front of it settles, the second fetch() doesn’t even start until the first one has fully finished downloading its data.
But do these two requests actually need to wait for each other? Not at all — they’re completely unrelated. So why are we forcing them to run one after another?
Kicking Them Off Together
This is where Promise.all() comes in. Instead of awaiting each request separately, we start both immediately and wait for all of them at once:
async function getUserAndPosts(userId) {
const [user, posts] = await Promise.all([
fetch(`https://api.example.com/users/${userId}`).then(res => res.json()),
fetch(`https://api.example.com/users/${userId}/posts`).then(res => res.json())
]);
return { user, posts };
}Both fetch() calls fire off one right after the other, with no waiting in between. Promise.all() takes an array of Promises and gives us back a single Promise that resolves once every Promise in that array has resolved, with the results in the exact same order we passed them in.
Since the two requests are now running concurrently instead of sequentially, the total time is roughly however long the slowest one takes, not the sum of both.
One Failure Ruins It For Everyone
Promise.all() is fail-fast: the moment any single Promise in the array rejects, Promise.all() immediately rejects too, with that same error, even if every other request already succeeded.
try {
const [user, posts] = await Promise.all([
getUser(userId),
getPostsThatMightFail(userId)
]);
} catch (error) {
console.error("One of them failed:", error);
// We have no idea if getUser() actually succeeded — its result is just gone
}If getUser() finished successfully half a second earlier, it doesn’t matter. That result is discarded the instant getPostsThatMightFail() rejects.
If you need to know the outcome of every request, success or failure, reach for Promise.allSettled() instead. It never rejects early — it waits for all of them and gives you back an array of { status, value } or { status, reason } objects:
const results = await Promise.allSettled([
getUser(userId),
getPostsThatMightFail(userId)
]);
results.forEach(result => {
if (result.status === "fulfilled") {
console.log("Got:", result.value);
} else {
console.log("Failed:", result.reason);
}
});The takeaway for today is that Promise.all() runs requests concurrently instead of one by one, but it’s all-or-nothing. When you need every result regardless of failures, Promise.allSettled() is the safer tool.
That’s 2Minutes Concept, see you in the next one !
Sources
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled