The new AggregateError object
Background of the AggregateError Object
ECMAScript 12 (ES2021) introduced the AggregateError
object to represent cases where multiple errors are wrapped into a single error. This feature is particularly useful for scenarios requiring the simultaneous throwing of multiple errors, such as when the Promise.any()
method needs to report all rejection reasons when all promises are rejected.
Basic Usage of AggregateError
The AggregateError
constructor takes two parameters: an array of errors and an optional message
string. It creates a new AggregateError
object containing all the provided errors.
const error1 = new Error('Error 1');
const error2 = new Error('Error 2');
const aggregate = new AggregateError([error1, error2], 'Multiple errors occurred');
console.log(aggregate.message); // "Multiple errors occurred"
console.log(aggregate.errors); // [Error: Error 1, Error: Error 2]
console.log(aggregate.errors[0].message); // "Error 1"
AggregateError and Promise.any()
Promise.any()
is a new method introduced in ES2021. It takes an array of promises and returns the value of the first resolved promise. If all promises are rejected, it throws an AggregateError
containing all rejection reasons.
const promise1 = Promise.reject(new Error('Failed 1'));
const promise2 = Promise.reject(new Error('Failed 2'));
Promise.any([promise1, promise2])
.catch(aggregateError => {
console.log(aggregateError instanceof AggregateError); // true
console.log(aggregateError.errors.length); // 2
console.log(aggregateError.errors[0].message); // "Failed 1"
console.log(aggregateError.errors[1].message); // "Failed 2"
});
Custom AggregateError
You can create custom subclasses of AggregateError
to add additional functionality or properties.
class CustomAggregateError extends AggregateError {
constructor(errors, message, extraInfo) {
super(errors, message);
this.extraInfo = extraInfo;
}
getErrorCount() {
return this.errors.length;
}
}
const errors = [new Error('E1'), new Error('E2')];
const customError = new CustomAggregateError(errors, 'Custom error', {code: 500});
console.log(customError.getErrorCount()); // 2
console.log(customError.extraInfo.code); // 500
Handling Errors with AggregateError
AggregateError
is particularly useful when handling multiple operations that might fail. Here's an example of batch processing files:
async function processFiles(filePaths) {
const errors = [];
const results = [];
for (const filePath of filePaths) {
try {
const content = await readFile(filePath);
results.push(processContent(content));
} catch (error) {
errors.push(error);
}
}
if (errors.length > 0) {
throw new AggregateError(errors, `Failed to process ${errors.length} files`);
}
return results;
}
try {
await processFiles(['file1.txt', 'file2.txt', 'file3.txt']);
} catch (error) {
if (error instanceof AggregateError) {
console.error(`Total errors: ${error.errors.length}`);
error.errors.forEach((err, index) => {
console.error(`Error ${index + 1}: ${err.message}`);
});
} else {
console.error('Unexpected error:', error);
}
}
Browser Compatibility of AggregateError
AggregateError
is widely supported in modern browsers, including:
- Chrome 85+
- Firefox 79+
- Safari 14+
- Edge 85+
- Node.js 15.0.0+
For unsupported environments, you can use a polyfill:
if (typeof AggregateError === 'undefined') {
class AggregateError extends Error {
constructor(errors, message) {
super(message);
this.errors = errors;
this.name = 'AggregateError';
}
}
globalThis.AggregateError = AggregateError;
}
Comparison of AggregateError with Other Error Types
Feature | AggregateError | Error | TypeError |
---|---|---|---|
Can contain multiple errors | Yes | No | No |
Primarily used for Promise.any() | Yes | No | No |
Can be subclassed | Yes | Yes | Yes |
Browser support | ES2021+ | ES1 | ES1 |
Practical Use Cases
- Batch API Requests: When making requests to multiple endpoints and collecting errors from all failed requests.
async function fetchMultipleUrls(urls) {
const promises = urls.map(url =>
fetch(url).catch(error => error)
);
const results = await Promise.all(promises);
const errors = results.filter(result => result instanceof Error);
if (errors.length > 0) {
throw new AggregateError(errors, 'Some requests failed');
}
return results;
}
- Form Validation: Collecting validation errors for all fields in a form.
function validateForm(formData) {
const errors = [];
if (!formData.username) {
errors.push(new Error('Username is required'));
}
if (formData.password.length < 8) {
errors.push(new Error('Password must be at least 8 characters'));
}
if (errors.length > 0) {
throw new AggregateError(errors, 'Form validation failed');
}
}
- Database Transactions: Recording all operation failures when rolling back a transaction.
async function executeTransaction(operations) {
const errors = [];
try {
await beginTransaction();
for (const op of operations) {
try {
await executeOperation(op);
} catch (error) {
errors.push(error);
}
}
if (errors.length > 0) {
await rollbackTransaction();
throw new AggregateError(errors, 'Transaction failed');
}
await commitTransaction();
} catch (error) {
if (!(error instanceof AggregateError)) {
await rollbackTransaction();
throw error;
}
}
}
Best Practices for AggregateError
- Error Messages: Provide meaningful aggregated error messages while preserving the original messages of individual errors.
- Error Count: Check the length of the
errors
array when handling anAggregateError
. - Error Types: Preserve the original error types; avoid converting all errors to generic
Error
objects. - Logging: When logging an
AggregateError
, ensure all sub-errors are logged. - Error Propagation: In middleware or high-level error handling, consider whether to split the
AggregateError
into individual errors.
// Good error handling example
try {
await someOperationThatMightThrowAggregateError();
} catch (error) {
if (error instanceof AggregateError) {
logger.error(`Operation failed with ${error.errors.length} errors:`);
error.errors.forEach((err, index) => {
logger.error(`Error ${index + 1}:`, err.stack || err.message);
});
// Decide whether to handle all errors or rethrow based on business needs
if (error.errors.some(err => err instanceof CriticalError)) {
throw error.errors.find(err => err instanceof CriticalError);
}
} else {
logger.error('Unexpected error:', error);
throw error;
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn