【问题标题】:Dojo - ReferenceError exception in promise being swallowedDojo - 吞下 Promise 中的 ReferenceError 异常
【发布时间】:2013-06-22 22:32:29
【问题描述】:

在 jQuery 中,如果您在 ajax 回调方法中出错,您将收到正确的控制台错误消息和堆栈跟踪。

$.get("https://api.github.com/users/octocat/orgs", function() {
  var a = FAIL;
});

然而,在使用 dojo/request/xhr 的 dojo 中,这些愚蠢的错误似乎被完全吞没了。当我运行它时,我的控制台中唯一的内容是“then”和“always”。

require(["dojo/request/xhr" ], function(xhr) {
    var promise = xhr.get("https://api.github.com/users/octocat/orgs");
    promise.then(function(data) {
        console.log('then');
        var a = FAIL;
        console.log('goodbye');
    }, function() {
        console.log('error');
    });
    promise.otherwise(function() {
        console.log('otherwise');
    });
    promise.always(function() {
        console.log('always');
    });
});

使用已弃用的dojo.xhrGet,问题得到了非常轻微的改善。我收到一条控制台错误消息,并调用了我的错误处理程序,但它只显示“ReferenceError {}”并为我提供了一个从不指向我拥有的函数的堆栈跟踪:

dojo.xhrGet({
    url: "https://api.github.com/users/octocat/orgs",
    load: function() {
        console.log('dojo.xhrGet.load');
        var a = FAIL;

        console.log('goodbye dojo.xhrGet.load');
    },
    error: function() {
        console.log('dojo.xhrGet.error');
    },
    handle: function() {
        console.log('dojo.xhrGet.handle');
    }
});

在编写程序时我们会犯错误,很高兴我们有像 chrome 开发者工具这样的工具来指出这些错误。当您可以看到堆栈跟踪和错误消息时,发现错误所需的时间显然比没有收到反馈要快得多。我在道场没有得到任何反馈,我不敢相信这样一个受欢迎的图书馆可以这样运作。我做错了什么?

【问题讨论】:

    标签: exception dojo promise deferred


    【解决方案1】:

    在 dojoConfig 中设置 useDeferredInstrumentation: true。这里是an example

            <script>
               var dojoConfig = {
                    useDeferredInstrumentation: true
                };
            </script>
            <script src="js/lib/dojo/dojo.js.uncompressed.js"></script>
    

    这会在 console.error 上提供相当实用的错误消息和堆栈跟踪输出:

    ReferenceError {} "ReferenceError: FAIL is not defined
        at http://fiddle.jshell.net/gNdCb/2/show/:25:17
        at signalListener (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14205:21)
        at signalWaiting (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14196:4)
        at resolve (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14360:5)
        at signalDeferred (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14249:15)
        at signalListener (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14220:6)
        at signalWaiting (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14196:4)
        at resolve (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14360:5)
        at signalDeferred (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14249:15)
        at signalListener (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14226:4)
        ----------------------------------------
        rejected at signalDeferred (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14252:15)
        at signalListener (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14223:5)
        at signalWaiting (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14196:4)
        at resolve (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14360:5)
        at signalDeferred (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14249:15)
        at signalListener (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14220:6)
        at signalWaiting (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14196:4)
        at resolve (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14360:5)
        at signalDeferred (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14249:15)
        at signalListener (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14226:4)
        ----------------------------------------
    Error
        at Promise.then.promise.then (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:14420:24)
        at http://fiddle.jshell.net/gNdCb/2/show/:23:13
        at runFactory (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:1117:43)
        at execModule (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:1245:5)
        at http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:812:7
        at guardCheckComplete (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:1260:5)
        at contextRequire (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:811:6)
        at req (http://ajax.googleapis.com/ajax/libs/dojo/1.9.0/dojo/dojo.js.uncompressed.js:137:11)
        at http://fiddle.jshell.net/gNdCb/2/show/:21:1" 
    

    【讨论】:

    • 当然,如果能够在 window.onerror 之类的文件中实际捕获或处理这些错误,那就太好了。有什么建议吗?
    【解决方案2】:

    您从 jQuery 继承的 Promise 的理解与其他人(查看 Promises/a+ 实现)的理解完全不同。对于这个答案的其余部分,我将讨论 promises/a+ 兼容的 promises。 Dojo 的 Deferred 实际上并不符合 a+ 标准,但它足够接近,以至于我在这里讨论的所有内容都同样适用。

    Promises 是不可变的,您不能通过调用 then 来更改 Promise 状态。一个承诺代表一个最终的价值,如果能够通过说“一旦价值准备好,就这样做”来改变承诺是荒谬的。

    那么,希望这可以解释为什么不调用您的错误处理程序,但是捕获错误的基本想法仍然是完全可能的。您只需要使用返回值。当你在一个 Promise 上调用 then 时,它会返回一个新的(几乎总是)不同的 Promise。这个新的 Promise 非常特别,如果原始的 Promise 被解析,并且你传递的成功处理程序被调用,并且返回一些东西,那将是第二个 Promise 的解析值。

    同样,如果错误处理程序(在第一个 promise 上)被触发,并且该函数返回一些东西,那么这个东西将是第二个 promise 的分辨率值。抛出的错误也是如此,它们被传递给错误处理程序(第二个承诺!)。

    所以这是你的第一个代码示例,它以更 Promise/a+ 的方式编写:

    require(["dojo/request/xhr" ], function(xhr) {
        var promise = xhr.get("https://api.github.com/users/octocat/orgs");
        promise.then(function(data) {
            console.log('then');
            var a = FAIL;
            console.log('goodbye');
        }, function() {
            console.log('error');
        }).then(null, function() {
            console.log('otherwise');
        });
    
        promise.always(function() {
            console.log('always');
        });
    });
    

    我不太明白你想用 always 函数做什么,所以我不确定该放在哪里。关于调用堆栈,我建议查看 Q Promise 库,它具有非常先进的异步调用堆栈支持。

    【讨论】:

    • 这是对正在发生的事情的一个很好的解释。这是对您的代码的轻微更改,它也提供了堆栈跟踪:jsfiddle.net/5rHM6 不幸的是,堆栈跟踪没有指向正确的行(它指向 console.error 行)。所以如果你想捕捉错误,这是正确的答案;如果您想找到错误,我倾向于选择我的答案。但是,似乎如果您确实发现了错误,则检测会被抑制,因此您无法两全其美。
    • 你说得对,它更像是一个消息传递服务(“有一个错误”),而不是一个带有堆栈跟踪的完整错误抛出服务。我会敦促您查看 Q 库,它是我所知道的最好的异步库,他们肯定会处理这个问题。
    【解决方案3】:

    我有非常具体的需求,因为我需要异常来命中浏览器实现的本机 catch 子句。没关系我为什么需要这个,但我使用了这样的东西:

    function scream(func) {
        return function() {
            var args = arguments;
            setTimeout(function(){
                func.apply(null, args);
            }, 0);
        };
    }
    

    然后,使用它

    var promise = xhr.get("https://api.github.com/users/octocat/orgs");
    promise.then(scream(function(data) {
        //do stuff
    }));
    

    通过使用 setTimeout,您可以在浏览器事件队列上执行该函数,从而使 dojo 无法吞下您的异常。但是,总的来说,这是一个糟糕的解决方案,因为:

    • 它改变了堆栈跟踪的一部分
    • 它将之前同步执行的部分代码更改为异步执行,从而改变程序行为
    • 您不能将多个 .then() 承诺对象链接到返回值,这是承诺的真正好处之一。

    无论如何,我只是将其作为选项呈现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-01
      • 2018-08-25
      • 2018-05-26
      • 2012-03-06
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多