Promise.all() rejects immediately when any single promise in the array rejects, causing an unhandled promise rejection if not properly caught. The entire operation fails even if other promises would have succeeded.
Promise.all() uses an "all or nothing" approach to handle multiple promises concurrently. When you pass an array of promises to Promise.all(), it returns a single promise that resolves only when all input promises have resolved successfully. However, if any single promise rejects, Promise.all() immediately rejects with that first rejection reason, abandoning the results of all other promises. This behavior can catch developers off guard, especially when they expect all promises to complete regardless of individual failures. The rejected promise from Promise.all() must be handled with a .catch() handler or try/catch block (when using async/await), otherwise it becomes an unhandled promise rejection that can crash Node.js applications in strict mode or when --unhandled-rejections=strict is set. The other promises in the array continue to execute in the background, but their results are lost and inaccessible through the Promise.all() return value. This can lead to wasted resources and incomplete operations if those promises had side effects that needed to complete.
Wrap your Promise.all() call in a try/catch block to handle rejections:
async function fetchData() {
try {
const results = await Promise.all([
fetch('https://api.example.com/users'),
fetch('https://api.example.com/posts'),
fetch('https://api.example.com/comments')
]);
console.log('All requests succeeded:', results);
return results;
} catch (error) {
console.error('One or more promises failed:', error);
// Handle the error appropriately
throw error;
}
}This catches the first rejection and prevents an unhandled promise rejection.
If not using async/await, attach a .catch() handler directly:
Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
])
.then(results => {
console.log('All promises resolved:', results);
})
.catch(error => {
console.error('Promise.all rejected:', error);
// Handle the rejection
});The .catch() handler receives the first rejection reason from any failed promise.
If you need all promises to complete regardless of individual failures, use Promise.allSettled() instead:
async function fetchAllData() {
const results = await Promise.allSettled([
fetch('https://api.example.com/users'),
fetch('https://api.example.com/posts'),
fetch('https://api.example.com/comments')
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index} succeeded:`, result.value);
} else {
console.error(`Promise ${index} failed:`, result.reason);
}
});
return results;
}Promise.allSettled() never rejects—it waits for all promises to settle and returns an array of objects with status ('fulfilled' or 'rejected') and value/reason.
Handle errors at the individual promise level before passing to Promise.all():
async function safeFetch(url) {
try {
const response = await fetch(url);
return { success: true, data: response };
} catch (error) {
return { success: false, error: error.message };
}
}
const results = await Promise.all([
safeFetch('https://api.example.com/users'),
safeFetch('https://api.example.com/posts'),
safeFetch('https://api.example.com/comments')
]);
// All promises always resolve, check success flag
const failed = results.filter(r => !r.success);
if (failed.length > 0) {
console.log('Some requests failed:', failed);
}This ensures Promise.all() never rejects while still tracking individual failures.
Understanding Promise.all() short-circuit behavior: When Promise.all() rejects, it does so immediately upon the first rejection, but this doesn't cancel the other promises. They continue executing in the background, which can lead to race conditions or wasted resources. If you need to cancel pending operations when one fails, consider implementing an AbortController pattern.
Process-level unhandled rejection handling: In Node.js, you can add a global handler for unhandled rejections as a last resort:
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
// Application specific logging, throwing an error, or other logic here
});However, this is not a replacement for proper error handling—always handle rejections at the point they occur.
Promise.all vs Promise.allSettled vs Promise.race: Choose the right method for your use case:
- Promise.all(): Use when all promises must succeed for the operation to be valid (e.g., loading critical dependencies)
- Promise.allSettled(): Use when you want results from all promises regardless of success/failure (e.g., batch API calls to third parties)
- Promise.race(): Use when you only need the first promise to settle (e.g., timeout patterns, redundant requests)
Performance consideration: Promise.all() is more efficient than sequential await calls when promises are independent, as they run concurrently. However, if one promise is likely to fail quickly, consider running it first to fail fast before starting expensive operations.
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files
Error: cluster.fork() failed (cannot create child process)
cluster.fork() failed - Cannot create child process