概述
AggregateError 是 JavaScript 中一个相对较新的错误类型,文章最后有兼容性介绍,它允许将多个错误包装在一个错误对象中。这个特性在处理多个可能失败的异步操作时特别有用,能够提供更清晰和结构化的错误处理机制。
为什么需要 AggregateError?
在异步编程中,我们经常需要同时处理多个操作:
javascript
// 传统方式 - 难以处理多个错误
try {
const result1 = await asyncOperation1();
const result2 = await asyncOperation2();
const result3 = await asyncOperation3();
} catch (error) {
// 只能捕获第一个错误
console.log(error);
}
使用 Promise.all() 时,如果多个 Promise 被拒绝,只能得到第一个错误:
javascript
Promise.all([
Promise.reject(new Error('错误1')),
Promise.reject(new Error('错误2')),
Promise.resolve('成功')
]).catch(error => {
// 只能得到 '错误1'
console.log(error.message);
});
AggregateError 解决了这个问题,它让我们能够收集和处理所有的错误。
语法
创建 AggregateError
javascript
new AggregateError(errors, message)
errors:一个可迭代对象(通常是数组),包含要聚合的错误message:可选的错误描述信息
基本示例
javascript
// 创建 AggregateError
const error1 = new Error('网络错误');
const error2 = new TypeError('类型错误');
const error3 = new RangeError('范围错误');
const aggregateError = new AggregateError(
[error1, error2, error3],
'多个操作失败'
);
console.log(aggregateError.message); // '多个操作失败'
console.log(aggregateError.errors); // [Error, TypeError, RangeError]
console.log(aggregateError.errors[0].message); // '网络错误'
主要使用场景
1. 与 Promise.any() 结合使用
Promise.any() 在所有 Promise 都被拒绝时抛出 AggregateError:
javascript
const promises = [
Promise.reject(new Error('失败1')),
Promise.reject(new Error('失败2')),
Promise.reject(new Error('失败3'))
];
Promise.any(promises)
.then(result => {
console.log('成功:', result);
})
.catch(error => {
if (error instanceof AggregateError) {
console.log('所有操作都失败了:');
error.errors.forEach((err, index) => {
console.log(`错误 ${index + 1}: ${err.message}`);
});
} else {
console.log('其他错误:', error.message);
}
});
2. 批量操作错误收集
javascript
async function processMultipleOperations(operations) {
const results = await Promise.allSettled(operations);
const errors = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
if (errors.length > 0) {
throw new AggregateError(errors, `${errors.length} 个操作失败`);
}
return results.map(result => result.value);
}
// 使用示例
const operations = [
fetch('/api/data1'),
fetch('/api/data2'),
fetch('/api/data3')
];
processMultipleOperations(operations)
.then(data => {
console.log('所有操作成功:', data);
})
.catch(error => {
if (error instanceof AggregateError) {
console.log('失败的操作:');
error.errors.forEach(err => {
console.log('-', err.message);
});
}
});
3. 数据验证
javascript
function validateUserData(userData) {
const errors = [];
if (!userData.email || !userData.email.includes('@')) {
errors.push(new Error('邮箱格式不正确'));
}
if (!userData.password || userData.password.length < 8) {
errors.push(new Error('密码必须至少8个字符'));
}
if (!userData.age || userData.age < 18) {
errors.push(new Error('年龄必须大于18岁'));
}
if (errors.length > 0) {
throw new AggregateError(errors, '用户数据验证失败');
}
return true;
}
// 使用
try {
validateUserData({
email: 'invalid-email',
password: '123',
age: 16
});
} catch (error) {
if (error instanceof AggregateError) {
console.log('验证错误:');
error.errors.forEach(err => {
console.log(`• ${err.message}`);
});
}
}
实际应用示例
文件上传批量处理
javascript
class FileUploader {
async uploadFiles(files) {
const uploadPromises = files.map(file => this.uploadSingleFile(file));
const results = await Promise.allSettled(uploadPromises);
const errors = [];
const successfulUploads = [];
results.forEach((result, index) => {
if (result.status === 'rejected') {
errors.push({
file: files[index].name,
error: result.reason
});
} else {
successfulUploads.push({
file: files[index].name,
url: result.value
});
}
});
if (errors.length > 0) {
const uploadErrors = errors.map(
err => new Error(`文件 ${err.file} 上传失败: ${err.error.message}`)
);
throw new AggregateError(uploadErrors, '部分文件上传失败');
}
return successfulUploads;
}
async uploadSingleFile(file) {
// 模拟上传逻辑
return new Promise((resolve, reject) => {
setTimeout(() => {
if (file.size > 5 * 1024 * 1024) { // 5MB
reject(new Error('文件大小超过限制'));
} else if (!file.type.startsWith('image/')) {
reject(new Error('只支持图片文件'));
} else {
resolve(`https://example.com/uploads/${file.name}`);
}
}, 1000);
});
}
}
// 使用
const uploader = new FileUploader();
const files = [
{ name: 'photo1.jpg', size: 1024000, type: 'image/jpeg' },
{ name: 'large-video.mp4', size: 10 * 1024 * 1024, type: 'video/mp4' },
{ name: 'document.pdf', size: 500000, type: 'application/pdf' }
];
uploader.uploadFiles(files)
.then(uploads => {
console.log('成功上传的文件:', uploads);
})
.catch(error => {
if (error instanceof AggregateError) {
console.log('上传失败的文件:');
error.errors.forEach(err => {
console.log(`- ${err.message}`);
});
console.log(`成功上传了 ${files.length - error.errors.length} 个文件`);
} else {
console.log('未知错误:', error.message);
}
});
浏览器兼容性
AggregateError 的兼容性如下:

兼容性处理
对于不支持的环境,可以使用 polyfill:
javascript
// 简单的 AggregateError polyfill
if (typeof AggregateError === 'undefined') {
class AggregateError extends Error {
constructor(errors, message) {
super(message);
this.name = 'AggregateError';
this.errors = errors;
}
}
window.AggregateError = AggregateError;
}
// 或者使用 core-js polyfill
import 'core-js/actual/aggregate-error';
特性检测
javascript
function supportsAggregateError() {
try {
new AggregateError([]);
return true;
} catch {
return false;
}
}
if (!supportsAggregateError()) {
// 降级方案
class SimpleAggregateError extends Error {
constructor(errors, message) {
super(message);
this.name = 'AggregateError';
this.errors = errors;
}
}
// 替换原生的 AggregateError
window.AggregateError = SimpleAggregateError;
}
最佳实践
1. 总是检查错误类型
javascript
try {
// 可能抛出 AggregateError 的代码
} catch (error) {
if (error instanceof AggregateError) {
// 处理聚合错误
console.log('多个错误:', error.errors.length);
} else {
// 处理单个错误
console.log('单个错误:', error.message);
}
}
2. 提供有意义的错误信息
javascript
// 好的做法
throw new AggregateError(
errors,
`操作失败: ${errors.length} 个子操作出错`
);
// 不好的做法
throw new AggregateError(errors, '错误');
3. 保持错误结构一致
javascript
function createAggregateError(failures, context) {
const errors = failures.map(failure =>
new Error(`${context}: ${failure.message}`)
);
return new AggregateError(errors, `${context} - ${failures.length} 个失败`);
}
总结
AggregateError对象代表了包装了多个错误对象的单个错误对象,它:
- 提供了结构化的多错误处理 - 能够将多个相关错误组织在一起
- 改善了异步编程体验 - 特别是在使用
Promise.any()和批量操作时 - 增强了错误信息的可读性 - 通过聚合相关的错误信息
- 促进了更好的错误处理模式 - 鼓励开发者考虑和处理所有可能的错误情况