the-front-end-knowledge-you-may-not-know icon indicating copy to clipboard operation
the-front-end-knowledge-you-may-not-know copied to clipboard

unhandledrejection 处理没有显式捕获的 Promise 异常

Open justjavac opened this issue 8 years ago • 15 comments

我们经常会写如下代码:

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 依然会有错误信息:

image

如果需要禁用此错误信息,需要在 unhandledrejection 事件处理函数中调用:

event.preventDefault();

相关链接

justjavac avatar Jul 05 '17 09:07 justjavac

如果使用es7的async来写代码

await asyncFunc()
func1()
func2()

此时asyncFunc reject了会怎么处理?不执行func1func2并且当成catch没有捕获?

erguotou520 avatar Jul 06 '17 01:07 erguotou520

@erguotou520 跟Promise的方式一样

try catch都能捕获es7的await返回的reject 为啥不统一用window.onerror处理,尴了个尬~~

taixw2 avatar Jul 06 '17 03:07 taixw2

try catch 写起来反而没promise方式好看,而且有的业务逻辑不适合使用全局onerror处理

erguotou520 avatar Jul 06 '17 03:07 erguotou520

@erguotou520

当 Promise 发生异常后,会从 .then 链进入到 .catch 链, 不过 Promise 链结束后依然存在异常,既Promise 依然是 reject 状态,Promise 坐在函数会 throw 一个 uncaughtException 异常,此异常一层一层向外传播,如果所有函数都没有捕获,则在 devtools 的 console 输出此错误。

image

相关阅读

justjavac avatar Jul 06 '17 03:07 justjavac

@erguotou520 我的意思是,与其新搞一个unhandledrejection事件处理Promise的异常 还不如直接用onerror处理

而且unhandledrejection/onerror应该统一用来处理那些漏掉的异常吧, 什么情况下会写业务逻辑上去。。

taixw2 avatar Jul 06 '17 04:07 taixw2

@taixw2 我们说的场景不一样,我指的是确实需要处理的异常,属于正常的异常。 按照2位的说法,应该是正常的流程都不用try catch,因为异常(确实是错误的异常)很少走到,这种异常用全局的错误捕获

erguotou520 avatar Jul 06 '17 05:07 erguotou520

看了你们的讨论,感觉unhandledrejectiononerror事件其实差别不大,都是用来捕获Promise中未捕获的reject异常。

unhandledrejectiononerror都是全局事件,正常情况下肯定不能用来写业务逻辑;那这两个事件的作用可能就只剩下“处理异常,不让异常抛出到控制台”,就像我一个同事说的那样,在控制台有大量飘红的输出显得很不专业。

其实个人感觉全局捕获异常不让输出到控制台可能显得干净,但是也不利于开发定位错误;还要一个不好的地方就是,如果要细粒度地针对情况处理每一种异常,就可能导致事件的回调变的很臃肿,执行时间更长。

我觉得最好是在每个Promise后都手动加上catch,会省去很多不必要的麻烦。而unhandledrejectiononerror这种兜底的方案,总感觉不是一个好方案;或者在单页应用中,针对每一个路由加载的页面,分别设置不同的unhandledrejectiononerror回调,在dom元素mounted的时候添加到window上,在页面销毁的时候卸载;每个页面都这么操作,既实现了兜底,又避免了全局注册一个回调,导致回调非常臃肿的问题。

Tao-Quixote avatar Jul 07 '17 09:07 Tao-Quixote

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);

Yangfan2016 avatar Sep 20 '18 03:09 Yangfan2016

https://drafts.csswg.org/css-writing-modes-4/#inline-base-direction

justjavac avatar Sep 20 '18 05:09 justjavac

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 avatar Nov 25 '19 07:11 w1301625107

@w1301625107 这里有提到 https://stackoverflow.com/questions/40026381/unhandledrejection-not-working-in-chrome 代码实际上是有效的.

EliazTray avatar Apr 30 '20 09:04 EliazTray

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');

baiheng1981 avatar Jun 01 '20 07:06 baiheng1981

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 该如何捕获

HereSinceres avatar Feb 03 '21 07:02 HereSinceres

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下会有输出。

huliyou avatar Jul 03 '21 10:07 huliyou

我也遇到了这个问题,发现问题在于引入的js资源于当前网址不同源导致的,unhandledrejection跟随同源策略

smz8023 avatar Dec 07 '22 09:12 smz8023