error事件不能兜底async异常,因其不处理promise拒绝;unhandledrejection事件才是捕获未处理promise拒绝的唯一标准通道,二者分工互补构成完整异常网关。

全局未捕获异常网关中,error 事件本身不能兜底 async 函数逸出的异常——它只处理同步错误、资源加载失败和部分回调错误,对 Promise 拒绝(包括所有 async/await 抛出但未处理的错误)完全无感。真正能捕获这类“边缘异常”的,是 unhandledrejection 事件,而非 error。
为什么 error 事件不是 async 异常的兜底项
async 函数内部抛错,本质是 Promise 被 reject。而 window.onerror(或 Node.js 的 process.on('error'))设计上就不监听 Promise rejection。即使你注册了 error 事件监听器,以下代码仍会触发 unhandledrejection,却不会进 error 回调:
async function f() { throw new Error('oops') }f(); // 未 await,也未 .catch() → 触发 unhandledrejection// window.onerror 不会执行
真正的二级兜底:unhandledrejection 是唯一标准通道
浏览器和 Node.js 都原生支持该事件,它是专为捕获“漏掉的 Promise 拒绝”而设:
- 浏览器:
window.addEventListener('unhandledrejection', e => { console.error(e.reason); }) - Node.js:
process.on('unhandledRejection', (reason, promise) => { /* 记录、上报、预警 */ }) - 必须在脚本最开始注册,否则初始化阶段的 async 错误会丢失
- event.reason 是原始拒绝值(可能是 Error、字符串、对象),可直接用于日志或监控上报
error + unhandledrejection 才构成完整网关
二者不是替代关系,而是分工互补:
- window.onerror:捕获同步语法错误、eval 报错、script 标签加载失败、setTimeout 中 throw 的错误等
- unhandledrejection:捕获所有未被 await、.catch() 或 try/catch 处理的 Promise 拒绝(即 async 函数逸出的全部边缘异常)
- 两者都应做基础日志记录,并对接 Sentry、Sentry、自建监控等系统
- 注意:Node.js v15+ 中,未监听 unhandledRejection 会导致进程直接退出,必须显式处理
别指望兜底代替主动防御
全局监听只是最后一道防线,无法还原原始调用栈、无法区分业务逻辑分支、也无法做重试或降级。关键仍在代码层:
- 每个可能失败的 await 前加 try/catch,不要全函数包裹
- fetch 后必须检查 res.ok,否则 404/500 不会触发 reject
- 并行请求优先用 Promise.allSettled,避免一个失败拖垮全部
- 对外暴露的 async 函数,若不 await,必须配 .catch(),不能依赖外层 try/catch











