如何调试JavaScript Promise?


75

我试图了解如何调试基于Promise的异步代码。承诺是指基于ECMAScript 6的承诺,而调试是指使用内置的chrome或firefox调试器。

我遇到的麻烦是-发生错误时,无论我如何“拒绝”它,我似乎都无法获取堆栈跟踪。

我尝试了这些:

console.log(new Error('Error occured'));
throw new Error('Throwing an Error');
return new Error('Error returned by the onRejected function');
reject(new Error('Pass Error to the reject function'));

但是这些都不返回代码或堆栈跟踪中的实际错误。

所以我的问题是-如何正确调试JavaScript Promises?


我宁愿基于html5rocks.com/en/tutorials/es6/promises进行操作:reject('Error')但是您可以发布jsfiddle,以便我们可以进行一些具体工作吗?
雷拉2014年

3
这是使用Bluebird promises库的主要优点之一。只要您的诺言代码中有未处理的拒绝或异常,它就会为您捕获堆栈跟踪。我一直在使用它与node.js,它节省了大量时间。老实说,我不知道如何通过内置的Promise获得相同的功能。
jfriend00 2014年

2
@Renra请不要给出不好的建议-抛出(或拒绝)原语是一种令人讨厌的习惯,会使调试变得困难得多。
Benjamin Gruenbaum 2014年

Answers:


61

这是一个值得讨论的好话题,可悲的消息是,使用本地承诺实际上很难。

在Chrome中调试原始ES6承诺太可怕了。这是因为它们将默默地抑制错误,并且每当您忽略捕获时,都不会给您任何表示诺言失败的迹象。更新:Chrome现在记录未处理的拒绝(有关方法,请参见此链接)

 Promise.resolve("foo").then(function(){
      throw new Error("You will never see this");// silent failure
 });

在Firefox中,由于它们执行未处理的拒绝检测,因此效果会更好一些-但是,它仍然是易碎品,如果您将诺言分配到任何地方都将无法使用。

那么,该怎么办呢?

包括Bluebird-它是ES6 Promise的超集,您可以在内部互换它,它具有更丰富的API,它更快,并且具有令人惊叹的堆栈跟踪。它的构建考虑了调试,并包括出色的错误处理功能。

包含Bluebird之后,请致电:

Promise.longStackTraces();

这会使它变慢一点(它仍然会非常快),并且会给您惊人的错误消息。例如:

Promise.resolve().then(function outer() {
    return Promise.resolve().then(function inner() {
        return Promise.resolve().then(function evenMoreInner() {
            a.b.c.d()
        });
    });
});

在本机承诺中-这将是一个无提示的失败,并且将很难调试-在Bluebird承诺中,默认情况下,它将在控制台中显示一个大的红色错误,从而为您提供:

ReferenceError: a is not defined
    at evenMoreInner (<anonymous>:6:13)
From previous event:
    at inner (<anonymous>:5:24)
From previous event:
    at outer (<anonymous>:4:20)
From previous event:
    at <anonymous>:3:9
    at Object.InjectedScript._evaluateOn (<anonymous>:581:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:540:52)
    at Object.InjectedScript.evaluate (<anonymous>:459:21)

完成调试后,您可以将其换出并返回本机Promise。就我个人而言,我很高兴知道自己在生产中存在错误,因此不建议这样做,但肯定是可行的。


1
可能减慢它的速度是因为它必须在创建堆栈时标记代码中的每个行号,这会使更长的代码稍微多一点。虽然用于调试,但这绝对值得减速。
尼尔

2
好吧,它需要将堆栈跟踪缝合在一起,但只能在出错时才能使用-通常,Bluebird速度非常快,并且在客户端使用长堆栈跟踪时性能仍然非常好。
Benjamin Gruenbaum 2014年

非常感谢您的回答,至少现在我知道我不是唯一一个为此而苦苦挣扎的人。肯定会调查蓝鸟。同样,将尝试制作一个JSFiddle来说明在promise失败时不显示任何错误。除此之外-发生错误的承诺实际上可能将其状态显示为“已解决”,我认为如果承诺不是链中的最后一个承诺,则将发生这种情况。
YemSalat 2014年

1
蓝鸟!太棒了 哇。我正在使用Q并感到非常沮丧。
RandallB 2015年

1
是的,Chrome实际上有一个很有前途的调试面板:)在金丝雀中,将很快更新
Benjamin Gruenbaum 2015年

14

这个答案是对本杰明·格鲁恩鲍姆(Benjamin Gruenbaum)的回答的补充:如果在promise链中使用catch语句,则会由于error.stack而获得堆栈跟踪:

        Promise.longStackTraces();

        function outer() {
            return Promise.resolve();
        }

        function inner() {
            return Promise.resolve();
        }

        function evenMoreInner() {
            a.b.c.d()
        }

        Promise.resolve()
            .then(outer)
            .then(inner)
            .then(evenMoreInner())
            .catch(function (err) {
                    console.log(err.message);
                    console.log(err.stack);
                });

错误信息:

ReferenceError: a is not defined
at evenMoreInner (test/test_promise.js:58:17)  <<<< HERE's the error!
at Context.<anonymous> (test/test_promise.js:64:23)

2
谢谢,丢失了此stack物业,想知道它去了哪里,哈哈!
NiCk Newman

4
只是要重申这一点……我被console.log(err)不包含err.stack的事实所愚弄。您需要console.log(err.stack)查看详细信息。
克里斯·丹尼斯

直到我勾选了山羊答案中提到的“异步”复选框后,它才对我有用。stackoverflow.com/a/25827268/247696
Flimm'5

13

*这并不能直接回答您的问题,但是仍然有帮助。

Chrome devtools最近获得了一项新功能,可用于调试异步代码,例如Promises。

http://www.html5rocks.com/zh-CN/tutorials/developertools/async-call-stack/

基本上,启用“来源”标签中的“异步”复选框,Chrome会为您重建调用堆栈,就好像它是同步代码一样。

屏幕截图


1
它仍然不会将错误输出到控制台,也不会提供有关错误发生位置的任何信息。错误是指您不打算“抓住”的事情,例如,可能在promise中发生的类型错误等
。– YemSalat

即使在承诺中,类型错误也应显示在控制台中。
dandavis 2014年

4
@dandavis不,不会。
Benjamin Gruenbaum 2014年

9
现在谷歌浏览器默认支持异步调用堆栈,并已删除的复选框:developers.google.com/web/updates/2017/05/...
丹·萨罗

1

他们似乎正在使用Chrome中的调试工具。有关更多信息,请参见此线程。

https://code.google.com/p/v8/issues/detail?id=3093

我没有检查过它是否已经在开发版或Beta版中,但我希望很快。然后可能会在2015年1月左右将其包含在普通版本中(只是个人猜测,绝对没有保证,因为我什至不为Google工作)。


我现在没有在Chrome中看到任何针对Promise的调试工具。你做?
Flimm

1
Google在几个月前删除了该功能。请尝试使用异步助手。查看此提交 关于异步调试
Johan Bergens

1

调试承诺的最佳方法是侦听您的unhandledRejection事件process

例如,这是设置和转储堆栈跟踪的方法...

 process.on('unhandledRejection', (reason, p) => {
   console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
   // Stack Trace
   console.log(reason.stack);
 });

您将代码放在哪里?我在诺言中出错,甚至找不到要查找的文件...
Stephane,

0

您可以在Promise对象的.then()函数中添加console.log()语句:如果需要,还可以添加.catch()。

genericDbCaller(dbName, collectionName, dbFunctionName, params) {
        return new Promise((resolve, reject) => {
            DatabaseContext.instance.getDbConn(dbName)
                .then((dbConn) => {
                    dbConn.get(collectionName)[dbFunctionName].apply(null, params)
                        .then(

                            (docs) =>{
                    ----->>>        console.log(docs);  
                            resolve(docs);
                        })
                        .catch((e)=>{
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.