如何在Promise中捕获未捕获的异常


76

有什么办法可以全局地捕获所有异常,包括Promise异常。例:

    window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
        alert("Error occured: " + errorMsg);//or any message
        return false;
    }

    var myClass = function(){

    }


    var pr = new Promise(function(resolve, react){
        var myInstance = new myClass();
        myInstance.undefinedFunction(); // this will throw Exception
        resolve(myInstance);
    });


    pr.then(function(result){
        console.log(result);
    });

    // i know right will be this:
    // pr.then(function(result){
    //     console.log(result);
    // }).catch(function(e){
    //     console.log(e);
    // });

该脚本将无提示地静默死。萤火虫中什么都没有。

我的问题是我是否犯了一个错误并忘记了抓住它,是否有办法在全球范围内抓住它?

Answers:


31

更新,本机承诺现在可以在大多数浏览器中执行以下操作:

window.addEventListener("unhandledrejection", function(promiseRejectionEvent) { 
    // handle error here, for example log   
});

前几天我们只是在讨论这个问题。

这是您使用bluebird的方法:

window.onpossiblyunhandledexception = function(){
    window.onerror.apply(this, arguments); // call
}

window.onerror = function(err){
    console.log(err); // logs all errors
}

使用Bluebird,也可以使用Promise.onPossiblyUnhandledRejectiondone不需要调用,因为该库将检测自身未处理的拒绝,这与Q不同(UPDATE 2016-我现在为Q编写了代码,并执行此操作)。

至于本地的承诺-他们最终要么window.onerror或新的处理程序,但是这些规范的过程还没有完成报告-你可以在这里遵循它


>在大多数浏览器中: 据MDN称,截至2018年6月,唯一支持此功能的浏览器是Chrome。
约翰·哈顿

1
根据statcounter, @ JohnHatton大多数浏览器都是Chrome。就是说-您始终可以使用Promise库以获得此行为。我在Safari和Edge中对Firefox(带有标记的标志)中的人执行了ping操作,以查看我们是否可以尽快在更多浏览器中实现此目的。
本杰明·格林鲍姆

1
“大多数浏览器都是Chrome浏览器”好吧,我想“大多数会话中使用的浏览器”是“大多数浏览器”的一种定义,而不是我会选择的一种。
约翰·哈顿

15

大多数promise实现当前不提供您所指的功能类型,但是许多第三方promise库(包括Qbluebird)提供了一种done()方法来捕获并抛出所有未捕获的错误,从而将其输出到控制台。

编辑:请参阅本杰明·格鲁恩鲍姆对Bluebird处理未捕获异常的功能的回答)

因此,如果您有此要求,则只需遵循经验法则,即您使用的任何诺言都应以以下方式返回或终止.done()

pr.then(function(result){
    console.log(result);
})
.done();

引用Q API参考:

done vs. then 用法的黄金法则 是:要么 return 您对其他人的承诺,要么如果链条以您结尾,请致电 done 终止。终止 catch 不足够,因为catch处理程序本身可能会引发错误。

我意识到这仍然需要一些额外的工作,您仍然可以忘记这样做,但这是对必须.catch()并明确处理每个错误的一种改进,尤其是出于上述原因。


2
其实做得还从单纯做任何不同.catch(thrower)(或做同样使用快捷如增加Promise.prototype.done = ...所以它是很没有意义的用于此目的,尤其是在蓝鸟其中未被捕获的错误通常是报道。
Esailija

1
@Esailija您能详细说明吗?thrower在这种情况下会是什么?
JLRishe

1
@Esailija好吧,Bluebird可能不需要它了,但是它仍然是防止错误在某些其他实现(AFAIK)中被完全吞噬的最简单方法,因此我不会将它称为毫无意义。
JLRishe

1
@JLRishe我认为他的观点是,人们不应该使用承诺的实现是燕子的错误(如jQuery,Q,$ Q等)和使用承诺库,让您跟踪未处理拒绝像蓝鸟,当,RSVP等
本杰明Gruenbaum

1
我的意思是说没有一个单独的概念,而不是
夸张的

5

在Node.js中,您可以使用:

process.on('unhandledRejection', (reason, promise) => {
  console.error(`Uncaught error in`, promise);
});

这在未知环境中进行调试时很有用,但如果您知道哪个承诺并具有访问权限,则应使用.catch.done具有更好的可维护性
tsuz

在较新版本的Node中,这实际上已成为标准行为。因此,即使没有添加这段代码,未记录的承诺中的错误也会被记录下来。
JussiR

您怎么能得到导致这一结果的诺言?或功能文件?问题是原因。堆栈始终是未定义的。
Loebre

使用promise(或setTimeout)会创建一个新的堆栈,因此您看不到从哪里调用promise。尽管较新版本的Chrome(V8?)可以使您看到以前的堆栈,也有助于调试异步代码。
JussiR

2

如果您要编写可在浏览器和Node.js环境中执行的代码,则可以将代码包装在这样的自执行函数中:

var isNode = (typeof process !== 'undefined' && typeof process.on !== 'undefined');
(function(on, unhandledRejection) {
    // PUT ANY CODE HERE
    on(unhandledRejection, function(error, promise) {
        console.error(`Uncaught error in`, promise);
    });
    // PUT ANY CODE HERE
})(
    isNode ? process.on.bind(process) : window.addEventListener.bind(window),
    isNode ? "unhandledRejection" : "unhandledrejection"
);

如果使用此代码会发生什么:

  • 如果要在Node.js环境中运行它,则处理程序将附加到流程对象,并在promise中有未捕获的异常时执行。

  • 如果要在浏览器环境中运行它,则处理程序将附加到window对象,并在promise中有未捕获的异常并且浏览器支持该unhandledrejection事件时执行该处理程序。

  • 如果您在不支持的浏览器环境中运行它unhandledrejection,则将无法捕获未捕获的异常,并且unhandledrejection事件处理程序将永远不会被触发,但是如果没有未捕获的异常,则不会出现任何错误。


1

如果您使用的是原生Promise,则非常简单。
您只需要在.catch某些地方拒绝即可。

ajax(request).catch(function(rejected){
      console.log(rejected);
});

如果我不在某个地方抓住它,那么未兑现的诺言将继续显示。但是如果我在某处发现它...


7
这不能回答所问的问题。
csauve
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.