the-front-end-knowledge-you-may-not-know
the-front-end-knowledge-you-may-not-know copied to clipboard
unhandledrejection 处理没有显式捕获的 Promise 异常
我们经常会写如下代码:
function main() {
asyncFunc()
.then(···)
.then(() => console.log('Done!'));
}
上面的代码有一处问题,就是异步 asyncFunc() 函数可能会 reject,这时我们并没有捕获 Promise 的 reject 分支。
我们可以使用 window.addEventListener('unhandledrejection', event => ···); 来处理所有 Promise 的异常情况。
根据 MDN 的兼容性表格看,好像目前只有 Chrome 支持这个事件。在 can i use 网站也搜不到关于 unhandledrejection 的相关信息。
除了这个事件之外,还有一个 rejectionhandled 事件:表示 rejection 当时并没有立即处理,在之后的一段时间内处理了,此时触发 rejectionhandled 事件。
关于 Promise 的 Rejection 处理,Google 的 Chrome 团队有个演示页面:Promise Rejection Events Sample
即使异常被处理了,DevTools 依然会有错误信息:

如果需要禁用此错误信息,需要在 unhandledrejection 事件处理函数中调用:
event.preventDefault();
相关链接
如果使用es7的async来写代码
await asyncFunc()
func1()
func2()
此时asyncFunc reject了会怎么处理?不执行func1和func2并且当成catch没有捕获?
@erguotou520 跟Promise的方式一样
try catch都能捕获es7的await返回的reject 为啥不统一用window.onerror处理,尴了个尬~~
try catch 写起来反而没promise方式好看,而且有的业务逻辑不适合使用全局onerror处理
@erguotou520
当 Promise 发生异常后,会从 .then 链进入到 .catch 链, 不过 Promise 链结束后依然存在异常,既Promise 依然是 reject 状态,Promise 坐在函数会 throw 一个 uncaughtException 异常,此异常一层一层向外传播,如果所有函数都没有捕获,则在 devtools 的 console 输出此错误。

相关阅读
- tc39/ecmascript-asyncawait#72
- nodejs/node#830
- domenic/unhandled-rejections-browser-spec
- Try/Catch always needed for await? - esdiscuss
@erguotou520 我的意思是,与其新搞一个unhandledrejection事件处理Promise的异常 还不如直接用onerror处理
而且unhandledrejection/onerror应该统一用来处理那些漏掉的异常吧, 什么情况下会写业务逻辑上去。。
@taixw2 我们说的场景不一样,我指的是确实需要处理的异常,属于正常的异常。 按照2位的说法,应该是正常的流程都不用try catch,因为异常(确实是错误的异常)很少走到,这种异常用全局的错误捕获
看了你们的讨论,感觉unhandledrejection和onerror事件其实差别不大,都是用来捕获Promise中未捕获的reject异常。
unhandledrejection和onerror都是全局事件,正常情况下肯定不能用来写业务逻辑;那这两个事件的作用可能就只剩下“处理异常,不让异常抛出到控制台”,就像我一个同事说的那样,在控制台有大量飘红的输出显得很不专业。
其实个人感觉全局捕获异常不让输出到控制台可能显得干净,但是也不利于开发定位错误;还要一个不好的地方就是,如果要细粒度地针对情况处理每一种异常,就可能导致事件的回调变的很臃肿,执行时间更长。
我觉得最好是在每个Promise后都手动加上catch,会省去很多不必要的麻烦。而unhandledrejection和onerror这种兜底的方案,总感觉不是一个好方案;或者在单页应用中,针对每一个路由加载的页面,分别设置不同的unhandledrejection和onerror回调,在dom元素mounted的时候添加到window上,在页面销毁的时候卸载;每个页面都这么操作,既实现了兜底,又避免了全局注册一个回调,导致回调非常臃肿的问题。
chrome :69 两个事件均无触发
window.addEventListener('unhandledrejection', event =>
{
console.log(event.reason); // 打印"Hello, Fundebug!"
});
window.addEventListener('rejectionhandled', event =>
{
console.log('rejection handled'); // 1秒后打印"rejection handled"
});
function foo()
{
return Promise.reject('Hello, Fundebug!');
}
var r = foo();
setTimeout(() =>
{
r.catch(e =>{});
}, 1000);
https://drafts.csswg.org/css-writing-modes-4/#inline-base-direction
chrome :69 两个事件均无触发
window.addEventListener('unhandledrejection', event => { console.log(event.reason); // 打印"Hello, Fundebug!" }); window.addEventListener('rejectionhandled', event => { console.log('rejection handled'); // 1秒后打印"rejection handled" }); function foo() { return Promise.reject('Hello, Fundebug!'); } var r = foo(); setTimeout(() => { r.catch(e =>{}); }, 1000);
请问找到原因了吗,困惑,是chrome的问题吗?
@w1301625107 这里有提到 https://stackoverflow.com/questions/40026381/unhandledrejection-not-working-in-chrome 代码实际上是有效的.
chrome :69 两个事件均无触发
window.addEventListener('unhandledrejection', event => { console.log(event.reason); // 打印"Hello, Fundebug!" }); window.addEventListener('rejectionhandled', event => { console.log('rejection handled'); // 1秒后打印"rejection handled" }); function foo() { return Promise.reject('Hello, Fundebug!'); } var r = foo(); setTimeout(() => { r.catch(e =>{}); }, 1000);请问找到原因了吗,困惑,是chrome的问题吗?
请问找到原因了吗? 我在vue和angular里作了这个例子打包后运行,angular能正常捕获,vue不能,怀疑是babel版本造成的,但没能找到解决方法。
window.addEventListener('unhandledrejection', event => {
console.error('window.Listener unhandledrejection: ', event.reason);
event.preventDefault();
});
window.addEventListener('rejectionhandled', event => {
console.error('window.Listener rejectionhandled: ', event.reason);
event.preventDefault();
});
window.addEventListener('error', event => {
console.error('window.Listener error: ', event);
event.preventDefault();
});
Promise.reject('Promise reject');
chrome :69 两个事件均无触发
window.addEventListener('unhandledrejection', event => { console.log(event.reason); // 打印"Hello, Fundebug!" }); window.addEventListener('rejectionhandled', event => { console.log('rejection handled'); // 1秒后打印"rejection handled" }); function foo() { return Promise.reject('Hello, Fundebug!'); } var r = foo(); setTimeout(() => { r.catch(e =>{}); }, 1000);请问找到原因了吗,困惑,是chrome的问题吗?
请问找到原因了吗? 我在vue和angular里作了这个例子打包后运行,angular能正常捕获,vue不能,怀疑是babel版本造成的,但没能找到解决方法。
window.addEventListener('unhandledrejection', event => { console.error('window.Listener unhandledrejection: ', event.reason); event.preventDefault(); }); window.addEventListener('rejectionhandled', event => { console.error('window.Listener rejectionhandled: ', event.reason); event.preventDefault(); }); window.addEventListener('error', event => { console.error('window.Listener error: ', event); event.preventDefault(); }); Promise.reject('Promise reject');
看一下core-js的版本,unhandledrejection 只对原生的promise有效,如果是polyfill,看一下polyfill中的promise 该如何捕获
chrome :69 两个事件均无触发
window.addEventListener('unhandledrejection', event => { console.log(event.reason); // 打印"Hello, Fundebug!" }); window.addEventListener('rejectionhandled', event => { console.log('rejection handled'); // 1秒后打印"rejection handled" }); function foo() { return Promise.reject('Hello, Fundebug!'); } var r = foo(); setTimeout(() => { r.catch(e =>{}); }, 1000);
直接在console里执行或者协议是file:///都不会触发,你运行在http localhost下会有输出。
我也遇到了这个问题,发现问题在于引入的js资源于当前网址不同源导致的,unhandledrejection跟随同源策略