如何正确处理 unhandledrejection 事件并确保脚本持续运行

`unhandledrejection` 是浏览器中全局分发的 dom 事件,用于捕获未被 catch 的 promise 拒绝;它**不会中断脚本执行**,仅作为调试与监控机制存在,不影响页面交互或后续代码运行。

unhandledrejection 事件常被误解为“作用域级错误”或“脚本终止信号”,但事实并非如此。这里的“global scope of a script”并非指 JavaScript 的词法作用域(如函数或块级作用域),而是指事件分发的目标对象——即全局执行环境的代表对象:在浏览器主线程中是 window,在 Web Worker 中是 self。该事件本质上是一个标准 DOM 事件,遵循事件冒泡规则(尽管它不冒泡到父元素,只在全局对象上触发),因此可通过 window.addEventListener('unhandledrejection', ...) 或简写 addEventListener('unhandledrejection', ...) 进行监听。

关键点在于:它不阻塞执行流,也不导致脚本崩溃。例如以下代码会正常输出三行日志,并继续执行后续语句:

addEventListener('unhandledrejection', (event) => {
  console.log('unhandledrejection');
  console.log('Promise:', event.promise);
  console.log('Reason:', event.reason);
  // ⚠️ 注意:调用 event.preventDefault() 可阻止默认行为(如控制台警告),
  // 但不会改变 Promise 已被拒绝的事实,也不会恢复执行。
});

Promise.reject('sss');
console.log('✅ 这行仍会执行'); // ✅ 正常输出

输出结果为:

unhandledrejection
Promise: Promise {: "sss"}
Reason: sss
✅ 这行仍会执行

这清晰表明:Promise 拒绝本身是异步操作,unhandledrejection 仅是事后通知机制;JavaScript 引擎继续调度任务、响应用户交互、执行定时器等——整个应用保持完全可用。

⚠️ 注意事项:

  • 该事件不可用于“挽救”已拒绝的 Promise(如自动 .catch())。它纯属观测用途,适合日志上报、错误监控或开发提醒;
  • 若需防止 unhandledrejection 触发,应在

    Promise 链末尾显式添加 .catch() 或 await 后包裹 try/catch;
  • 在 Node.js 环境中行为不同:v15+ 默认将未处理拒绝视为致命错误并退出进程(可通过 process.on('unhandledRejection', ...) 捕获并 process.exitCode = 0 避免退出);
  • 浏览器中即使不监听该事件,脚本也绝不会因此停止运行——最多仅在开发者工具控制台显示黄色警告。

总结:unhandledrejection 是一个诊断性、非侵入式的全局事件,设计初衷是帮助开发者发现遗漏的错误处理逻辑,而非控制程序生命周期。合理使用它可提升健壮性,但切勿将其误认为执行屏障或异常拦截层。