
Understanding JavaScript Promise.all With Real Examples
JavaScript is single-threaded, but that doesn’t mean it has to do one thing at a time. Thanks to the event loop and promises, we can run multiple asynchronous operations concurrently. One of the most useful tools for that is Promise.all.
Whether you’re fetching data from several APIs, reading multiple files, or running independent computations, Promise.all lets you start them all at once and wait for every one to finish.
What Is Promise.all?
Promise.all takes an iterable of promises and returns a single promise. That returned promise resolves when all of the input promises resolve, or rejects as soon as any one of them rejects.
const p1 = fetch('/api/users');
const p2 = fetch('/api/posts');
const p3 = fetch('/api/comments');
const results = await Promise.all([p1, p2, p3]);
If all three fetches succeed, results is an array of their resolved values, in the same order as the input array. If any fetch fails, the entire Promise.all rejects immediately.
Why Use It?
Without Promise.all, you might write code like this:
const users = await fetch('/api/users');
const posts = await fetch('/api/posts');
const comments = await fetch('/api/comments');
This waits for each request to finish before starting the next one. If each request takes 100ms, the total time is roughly 300ms. With Promise.all, all three start at the same time, so the total time is closer to 100ms.
const [users, posts, comments] = await Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments'),
]);
That’s a big performance win when the tasks are independent.
A Practical Example
Let’s say you’re building a dashboard that needs profile data, recent orders, and notifications.
async function loadDashboard() {
try {
const [profile, orders, notifications] = await Promise.all([
fetch('/api/profile').then(r => r.json()),
fetch('/api/orders').then(r => r.json()),
fetch('/api/notifications').then(r => r.json()),
]);
renderProfile(profile);
renderOrders(orders);
renderNotifications(notifications);
} catch (error) {
showErrorMessage('Failed to load dashboard data.');
}
}
All three requests fire in parallel. The dashboard only renders once everything is ready, and a single catch block handles any failure.
Error Handling
The default behavior of Promise.all is “fail fast.” As soon as one promise rejects, the whole operation rejects. This is usually what you want when every result is required.
But sometimes you want every task to finish, even if some fail. In those cases, Promise.allSettled is a better choice.
const results = await Promise.allSettled([
fetch('/api/a'),
fetch('/api/b'),
fetch('/api/c'),
]);
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Success:', result.value);
} else {
console.error('Failed:', result.reason);
}
});
Promise.allSettled never rejects. It returns an array of objects describing whether each promise fulfilled or rejected.
Common Pitfalls
One mistake is forgetting to handle the case where the input array is empty.
const results = await Promise.all([]);
console.log(results); // []
An empty array resolves immediately to an empty array. That’s fine, but it can surprise you if you expect at least one result.
Another issue is passing non-promise values. Promise.all wraps them in Promise.resolve, so this works:
await Promise.all([1, 2, Promise.resolve(3)]);
// [1, 2, 3]
But in real code, it’s clearer to keep every item a real promise.
Promise.all vs. Promise.race and Promise.any
Promise.allwaits for every promise to resolve, or rejects on the first failure.Promise.racesettles as soon as the first promise settles, whether it resolves or rejects.Promise.anyresolves as soon as the first promise resolves, ignoring rejections unless every promise rejects.
Choose the one that matches your use case. For parallel independent tasks where you need all results, Promise.all is usually the right tool.
Final Thoughts
Promise.all is one of those JavaScript utilities that seems simple but unlocks much cleaner and faster async code. It encourages you to think in terms of independent operations, reduces unnecessary waiting, and keeps error handling centralized.
Next time you find yourself chaining several await calls that don’t depend on each other, reach for Promise.all instead.