为什么 unhandledrejection 监听不到 Promise 错误?
我在项目里加了 window.addEventListener('unhandledrejection', ...),但有些 Promise 报错根本没触发这个监听,控制台报红了但监控没收到。明明是没 catch 的 reject,为啥不走 unhandledrejection?
比如下面这段代码,本地测试完全没触发监听:
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('test unhandled'));
}, 100);
});
先说你给的代码例子不触发
unhandledrejection的原因:因为你的 reject 是在 setTimeout 里异步执行的,这时候 Promise 已经不在调用栈里了。根据规范,这种延迟 reject 的情况不会被视为 "unhandled"。解决方法其实很简单,主要有两种方式:
第一种方式是直接给 Promise 加个 catch:
第二种方式更推荐,使用
window.onunhandledrejection而不是 addEventListener:这里有几个需要注意的技术细节:
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。