在 async/await 中,错误需通过继承 error 创建子类(如 contextualerror)并注入上下文字段(如 userid、traceid)来携带上下文;应使用 cause 链保留原始错误,并在源头注入而非依赖外层兜底。

在 async/await 流程中,错误本身不会自动“传递上下文”,但你可以通过包装错误、保留堆栈、显式捕获并重新抛出等方式,让错误携带更多上下文信息(比如请求 ID、用户 ID、操作路径等),便于排查和监控。
用 Error 子类封装上下文
原生 Error 对象不支持直接扩展属性,但你可以继承它,并在构造时注入上下文字段:
class ContextualError extends Error {
constructor(message, context = {}) {
super(message);
this.name = 'ContextualError';
this.context = { ...context };
// 保留原始堆栈(重要)
this.stack = new Error().stack;
}
}
// 使用示例
async function fetchUser(id) {
try {
const res = await fetch(`/api/user/${id}`);
if (!res.ok) throw new ContextualError('API returned error', {
userId: id,
endpoint: '/api/user',
timestamp: Date.now()
});
return await res.json();
} catch (err) {
// 错误已自带上下文,可直接抛出或记录
throw err;
}
}
在 await 链中保持上下文不丢失
避免只写 throw err 而丢掉原始错误细节;推荐用 throw new ContextualError(..., { cause: err })(Node.js 16.9+ / 现代浏览器支持)或手动挂载 cause 属性:
- 捕获后不要只抛新消息,要保留原始错误的 stack 和 message
- 用 err.cause = originalErr 显式链接错误链
- 日志工具(如 pino、winston)能自动展开 cause 链,显示完整上下文
统一中间件/包装器注入请求上下文
在 Express、Koa 或自定义异步入口处,把上下文(如 traceId、userId)存入 async_hooks 或 Promise 域(Node.js),或更简单地——用闭包/参数透传:
async function withContext(context, fn) {
try {
return await fn();
} catch (err) {
throw new ContextualError(err.message, { ...context, cause: err });
}
}
// 调用
await withContext({ traceId: 'abc123', action: 'login' }, () => login(user));
避免常见陷阱
- 不要在 catch 块里只写 console.error(err) 就结束 —— 上下文会断掉
- 避免 await Promise.all([...]) 吞掉单个失败项的上下文;改用 Promise.allSettled 并逐个检查
- 不要依赖 try/catch 外层统一兜底来补上下文 —— 最好在源头就带上,越靠近错误发生点越好











