在 async 逻辑中应抛出继承 error 的自定义错误类以保留堆栈、支持 instanceof 判断和监控;封装错误时用 cause 属性保留根因;调用处必须显式捕获 promise rejection。

在 async 逻辑中抛出自定义错误对象,核心是“用标准方式 throw,但错误本身要可识别、可携带上下文”。不是所有 throw 都能被合理捕获或分类处理,尤其在 Promise 链和 await 场景下。
必须用 Error 实例(别 throw 字符串或普通对象)
async/await 的错误传播机制依赖于 Promise rejection,而只有 Error 实例 才能正确保留堆栈信息、被 try/catch 捕获、并被 instanceof 判断类型。直接 throw 字符串或 { message } 对象会导致:
- 堆栈丢失,调试困难
- 无法用
err instanceof CustomError区分错误类型 - 部分监控工具(如 Sentry)可能忽略非 Error 对象
推荐:继承 Error 创建自定义类
这样既能保持标准行为,又能扩展字段。例如验证失败时:
class ValidationError extends Error {
constructor(message, field, value) {
super(message);
this.name = 'ValidationError';
this.field = field;
this.value = value;
this.code = 400;
}
}
async function createUser(userData) {
if (!userData.email) {
throw new ValidationError('邮箱不能为空', 'email', userData.email);
}
// ... 其他逻辑
}
在 try/catch 中 throw 新错误时,保留原始原因
异步操作中常需封装底层错误(比如 fetch 失败后转为业务错误)。这时建议用 cause 属性(ES2022+ 支持)或自定义字段保留原始错误:
- 现代写法(推荐):
throw new BusinessError('创建用户失败', { cause: err }); - 兼容写法:
const error = new BusinessError('创建用户失败'); error.cause = err; throw error;
后续可通过 err.cause 追溯根因,不影响日志和告警的准确性。
确保调用处真正捕获了 Promise rejection
async 函数返回 Promise,如果没被 await + try/catch 或 .catch() 处理,错误会变成 unhandledrejection。常见陷阱:
- 按钮 onclick="myAsyncFn()" → 错误静默丢失
- 事件监听器里调用 async 函数但没接 .catch()
- 顶层 await 被忽略,或未监听
unhandledrejection
安全做法是在调用点显式处理:
button.addEventListener('click', async () => {
try {
await createUser({ name: 'Alice' });
} catch (err) {
if (err instanceof ValidationError) {
showFieldError(err.field, err.message);
} else {
showToast('操作失败,请重试');
console.error(err);
}
}
});











