为什么 unhandledrejection 监听不到 Promise 错误?

Code°法霞 阅读 9

我在项目里加了 window.addEventListener('unhandledrejection', ...),但有些 Promise 报错根本没触发这个监听,控制台报红了但监控没收到。明明是没 catch 的 reject,为啥不走 unhandledrejection?

比如下面这段代码,本地测试完全没触发监听:

new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('test unhandled'));
  }, 100);
});
我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
迷人的子涵
这个问题其实挺常见的,我来解释下为什么会出现这种情况以及怎么解决。

先说你给的代码例子不触发 unhandledrejection 的原因:因为你的 reject 是在 setTimeout 里异步执行的,这时候 Promise 已经不在调用栈里了。根据规范,这种延迟 reject 的情况不会被视为 "unhandled"。

解决方法其实很简单,主要有两种方式:

第一种方式是直接给 Promise 加个 catch:
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('test unhandled'));
}, 100);
}).catch(e => console.log('Caught:', e)); // 这样就能捕获到错误了


第二种方式更推荐,使用 window.onunhandledrejection 而不是 addEventListener:
window.onunhandledrejection = function(event) {
console.log('Unhandled rejection:', event.reason);
event.preventDefault(); // 防止默认打印到控制台
};


这里有几个需要注意的技术细节:
1. 使用 onunhandledrejection 比 addEventListener 更可靠,因为有些浏览器实现会优先处理前者
2. 一定要调用 event.preventDefault(),否则浏览器还是会打印错误到控制台
3. 如果是 Node.js 环境,需要用 process.on('unhandledRejection')

背后的原理是这样的:浏览器判断一个 Promise 是否 unhandled 是根据 reject 时是否还有 then/catch 处理程序。在 setTimeout 里 reject 时,调用栈已经清空了,所以浏览器认为这个 reject 是 "handled" 的。

我遇到过类似问题,调试了整整一个下午才发现是这个原因... 后来我都养成了习惯,任何 Promise 都至少加个空 catch,不然某些错误会莫名其妙丢失。

还有一点要提醒:如果你用 async/await 语法,记得用 try/catch 包起来,否则抛出的错误也不会触发 unhandledrejection。
点赞 1
2026-03-09 11:16