Promise.allSettled()
Basic Concept of Promise.allSettled()
ECMAScript 2020 (ES11) introduced the Promise.allSettled()
method, which takes an array of Promises as input and returns a new Promise. This new Promise resolves only when all input Promises have settled (either fulfilled or rejected). Unlike Promise.all()
, Promise.allSettled()
does not terminate early if any Promise is rejected.
const promises = [
Promise.resolve(1),
Promise.reject('Error occurred'),
Promise.resolve(3)
];
Promise.allSettled(promises)
.then(results => {
results.forEach((result, index) => {
console.log(`Promise ${index}:`, result.status);
});
});
// Output:
// Promise 0: fulfilled
// Promise 1: rejected
// Promise 2: fulfilled
Return Value Structure
When the Promise returned by Promise.allSettled()
resolves, it yields an array of result objects, each containing the following properties:
status
: A string indicating the Promise's state ("fulfilled" or "rejected")value
: Present when status is "fulfilled," representing the resolved valuereason
: Present when status is "rejected," representing the rejection reason
const promises = [
Promise.resolve('Success'),
Promise.reject(new Error('Failure')),
Promise.resolve('Another success')
];
Promise.allSettled(promises)
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Value:', result.value);
} else {
console.log('Reason:', result.reason.message);
}
});
});
// Output:
// Value: Success
// Reason: Failure
// Value: Another success
Differences from Promise.all()
Promise.all()
and Promise.allSettled()
exhibit significant differences in handling Promise collections:
-
Behavioral Differences:
Promise.all()
rejects immediately if any Promise rejectsPromise.allSettled()
waits for all Promises to settle
-
Result Differences:
Promise.all()
resolves to an array of valuesPromise.allSettled()
resolves to an array of objects containing status information
// Promise.all() example
Promise.all([
Promise.resolve(1),
Promise.reject('Error'),
Promise.resolve(3)
])
.catch(error => {
console.log('Promise.all caught:', error); // Output: Promise.all caught: Error
});
// Promise.allSettled() example
Promise.allSettled([
Promise.resolve(1),
Promise.reject('Error'),
Promise.resolve(3)
])
.then(results => {
console.log('All settled:', results);
/* Output:
All settled: [
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: 'Error' },
{ status: 'fulfilled', value: 3 }
]
*/
});
Practical Use Cases
Promise.allSettled()
is particularly useful in the following scenarios:
- Batch Request Processing: When multiple independent requests need to be sent, and failure of one should not affect others
async function fetchMultipleUrls(urls) {
const requests = urls.map(url =>
fetch(url)
.then(response => response.json())
.catch(error => ({ error: true, message: error.message }))
);
const results = await Promise.allSettled(requests);
return results.map(result => {
if (result.status === 'fulfilled') {
return { data: result.value };
} else {
return { error: result.reason };
}
});
}
// Usage example
const urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'invalid-url'
];
fetchMultipleUrls(urls).then(results => {
results.forEach((result, index) => {
console.log(`URL ${index}:`, result.data || result.error);
});
});
- Multi-field Form Validation: When all field validation results need to be collected without stopping at the first failure
function validateFields(fields) {
const validations = fields.map(field => {
return new Promise((resolve) => {
// Simulate async validation
setTimeout(() => {
if (field.value.length >= field.minLength) {
resolve({ ...field, valid: true });
} else {
resolve({ ...field, valid: false, error: `Minimum ${field.minLength} characters` });
}
}, Math.random() * 1000);
});
});
return Promise.allSettled(validations)
.then(results => results.map(result => result.value));
}
// Usage example
const formFields = [
{ name: 'username', value: 'john', minLength: 3 },
{ name: 'password', value: '123', minLength: 6 },
{ name: 'email', value: 'john@example.com', minLength: 5 }
];
validateFields(formFields).then(validationResults => {
validationResults.forEach(result => {
console.log(`${result.name}:`, result.valid ? 'Valid' : `Invalid - ${result.error}`);
});
});
Error Handling Strategies
When using Promise.allSettled()
, different strategies can be employed to handle rejected Promises:
- Filtering Out Failed Promises:
const promises = [
Promise.resolve('Data 1'),
Promise.reject('Error 1'),
Promise.resolve('Data 2'),
Promise.reject('Error 2')
];
Promise.allSettled(promises)
.then(results => {
const successfulResults = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
const errors = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
console.log('Successes:', successfulResults);
console.log('Errors:', errors);
});
// Output:
// Successes: ["Data 1", "Data 2"]
// Errors: ["Error 1", "Error 2"]
- Retrying Failed Promises:
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
async function fetchImportantData(urls) {
const initialAttempts = urls.map(url =>
fetch(url).then(r => r.json()).catch(() => null)
);
const initialResults = await Promise.allSettled(initialAttempts);
const needsRetry = initialResults
.filter((result, index) => result.status === 'rejected')
.map((_, index) => urls[index]);
const retryResults = await Promise.allSettled(
needsRetry.map(url => fetchWithRetry(url))
);
// Merge results...
}
Performance Considerations
Although Promise.allSettled()
waits for all Promises to settle, performance-sensitive scenarios require attention:
- Parallel Execution: All Promises still execute in parallel and do not become sequential due to
allSettled()
- Memory Usage: A large number of concurrent Promises may cause memory pressure
- Timeout Control: Can be combined with
Promise.race()
to implement timeout mechanisms
function withTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
async function processTasks(tasks) {
const promises = tasks.map(task =>
withTimeout(task.execute(), task.timeout || 5000)
.then(value => ({ task, status: 'fulfilled', value }))
.catch(reason => ({ task, status: 'rejected', reason }))
);
const results = await Promise.allSettled(promises);
// Process results...
}
Browser Compatibility and Polyfill
While modern browsers generally support Promise.allSettled()
, older environments may require a polyfill:
if (!Promise.allSettled) {
Promise.allSettled = function(promises) {
return Promise.all(promises.map(p =>
Promise.resolve(p).then(
value => ({ status: 'fulfilled', value }),
reason => ({ status: 'rejected', reason })
)
));
};
}
For TypeScript users, type definitions can be added:
interface PromiseFulfilledResult<T> {
status: 'fulfilled';
value: T;
}
interface PromiseRejectedResult {
status: 'rejected';
reason: any;
}
type PromiseSettledResult<T> = PromiseFulfilledResult<T> | PromiseRejectedResult;
declare global {
interface PromiseConstructor {
allSettled<T>(promises: Iterable<Promise<T>>): Promise<PromiseSettledResult<T>[]>;
}
}
Integration with Other Async Patterns
Promise.allSettled()
can be combined with other async patterns:
- With async/await:
async function processMultipleTasks(tasks) {
const results = await Promise.allSettled(
tasks.map(task => task.execute())
);
const report = {
successCount: results.filter(r => r.status === 'fulfilled').length,
failureCount: results.filter(r => r.status === 'rejected').length,
errors: results
.filter(r => r.status === 'rejected')
.map(r => r.reason.message)
};
return report;
}
- With Generator Functions:
function* taskGenerator(count) {
for (let i = 0; i < count; i++) {
yield new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.3 ? resolve(`Task ${i} completed`) : reject(`Task ${i} failed`);
}, Math.random() * 1000);
});
}
}
async function runTasks(count) {
const tasks = [...taskGenerator(count)];
const results = await Promise.allSettled(tasks);
results.forEach((result, i) => {
console.log(`Task ${i}:`,
result.status === 'fulfilled'
? result.value
: `Failed - ${result.reason}`
);
});
}
runTasks(5);
Practices in Complex Systems
In large-scale applications, Promise.allSettled()
helps build more robust systems:
- Microservice Calls:
class ServiceOrchestrator {
constructor(services) {
this.services = services;
}
async gatherData(request) {
const servicePromises = this.services.map(service =>
service.fetchData(request)
.then(data => ({ service: service.name, data }))
.catch(error => ({ service: service.name, error }))
);
const results = await Promise.allSettled(servicePromises);
return results.map(result => {
if (result.status === 'fulfilled') {
return {
service: result.value.service,
status: 'success',
data: result.value.data
};
} else {
return {
service: result.reason.service,
status: 'error',
error: result.reason.error
};
}
});
}
}
// Usage example
const orchestrator = new ServiceOrchestrator([
{ name: 'userService', fetchData: fetchUser },
{ name: 'productService', fetchData: fetchProducts },
{ name: 'inventoryService', fetchData: fetchInventory }
]);
orchestrator.gatherData({ userId: 123 }).then(report => {
console.log('Service Report:', report);
});
- Data Aggregation:
async function aggregateDataSources(sources) {
const dataPromises = sources.map(async source => {
try {
const rawData = await fetch(source.url);
const data = await rawData.json();
return {
source: source.name,
status: 'success',
data: source.transform ? source.transform(data) : data
};
} catch (error) {
return {
source: source.name,
status: 'error',
error: error.message
};
}
});
const results = await Promise.allSettled(dataPromises);
return {
successful: results
.filter(r => r.status === 'fulfilled' && r.value.status === 'success')
.map(r => r.value),
failed: results
.filter(r => r.status === 'fulfilled' && r.value.status === 'error')
.map(r => r.value)
};
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn