unhandledrejection 事件不能重写 await,但可作为兜底机制捕获未处理的 promise rejection,配合上下文注入、错误分类、safeawait 包装器及自动恢复策略,实现统一可观测与降级。

全局未捕获异常网关中,unhandledrejection 事件本身不能“重写 await”,但可以配合 Promise 链控制、错误拦截与降级策略,在 await 报错逃逸后做统一兜底和可观测处理。关键不是改变 JavaScript 异步执行模型,而是让逃逸的 Promise rejection 被捕获、标记、上报,并避免默认崩溃或静默失败。
理解 await 逃逸的本质
await 后跟一个 rejected Promise,若未被 try/catch 包裹,该 rejection 会变成未处理的 Promise rejection,触发 unhandledrejection 事件。这不是“错误逃逸”,而是 Promise 错误传播机制的正常表现——它只是没被局部消化。
- Promise rejection 不等于 throw,它不会中断同步栈,但会触发异步错误流
-
unhandledrejection是最后一道防线,仅对“完全没人监听”的 rejection 生效 - 一旦在任意层级用
.catch()或try/catch捕获,就不会进入该事件
在网关层统一拦截并增强错误上下文
在应用入口(如 main.js 或框架初始化处)监听 unhandledrejection,补充业务上下文、打标来源、阻止默认行为(如刷新/白屏),再主动上报:
- 调用
event.preventDefault()阻止浏览器默认提示(非必须,但利于体验统一) - 从
event.reason提取原始 error,用error.stack和event.promise关联异步链路 - 注入当前路由、用户 ID、请求 ID 等上下文字段,便于问题定位
- 区分错误类型:网络超时、业务校验失败、第三方 SDK 报错等,做不同上报通道或告警级别
用 Promise 包装器实现“自动兜底”模式
不依赖开发者每个 await 都写 try/catch,可在工具函数层封装带默认错误处理的异步调用:
async function safeAwait(promise, fallback = null) {
try {
return await promise;
} catch (err) {
// 主动捕获,避免触发 unhandledrejection
reportError(err, { source: 'safeAwait' });
return fallback;
}
}
- 在 API 请求、组件加载、定时任务等高频异步场景中强制使用该包装器
- fallback 可返回默认值、空对象、loading 状态,而非抛出或静默
- 保留原始堆栈:通过
err.cause或自定义属性携带原始 Promise 的 traceId
结合监控与自动恢复能力
unhandledrejection 不应只用于日志,还可触发轻量级恢复逻辑:
- 检测是否为网络类错误(如
TypeError: Failed to fetch),自动重试一次 - 若发生在关键页面(如支付页),弹出友好提示并提供“刷新”或“返回首页”按钮
- 记录连续失败次数,达到阈值后主动降级 UI(如隐藏非核心模块)
- 上报时附带
navigator.onLine、performance.memory等环境指标,辅助归因
不复杂但容易忽略:真正有效的异常网关,不是把所有 await 都套 try/catch,而是分层设防——局部用包装器收敛错误,全局用 unhandledrejection 做漏网之鱼捕获与增强分析,再叠加业务语义的响应动作。










