Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
justjavac opened this issue Jul 5, 2017 · 15 comments
Open

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

justjavac opened this issue Jul 5, 2017 · 15 comments
Labels

Comments

@justjavac
Copy link
Owner

justjavac commented Jul 5, 2017

我们经常会写如下代码:

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

相关链接

@erguotou520
Copy link

erguotou520 commented Jul 6, 2017

如果使用es7的async来写代码

await asyncFunc()
func1()
func2()

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

@taixw2
Copy link

taixw2 commented Jul 6, 2017

@erguotou520 跟Promise的方式一样

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

@erguotou520
Copy link

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

@justjavac
Copy link
Owner Author

@erguotou520

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

image

相关阅读

@taixw2
Copy link

taixw2 commented Jul 6, 2017

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

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

@erguotou520
Copy link

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

@justjavac justjavac mentioned this issue Jul 6, 2017
@Tao-Quixote
Copy link

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

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

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

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

@Yangfan2016
Copy link

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

@justjavac
Copy link
Owner Author

@w1301625107
Copy link

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的问题吗?

@EliazTray
Copy link

@baiheng1981
Copy link

baiheng1981 commented Jun 1, 2020

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

@HereSinceres
Copy link

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

@huliyou
Copy link

huliyou commented Jul 3, 2021

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

@smz8023
Copy link

smz8023 commented Dec 7, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests