【问题标题】:Bluebird (or other Promise library) Keep Promise Error Stack TracesBluebird(或其他 Promise 库)保留 Promise 错误堆栈跟踪
【发布时间】:2016-05-14 10:38:31
【问题描述】:

好吧,我可能只是错过了显而易见的事情,但我似乎找不到这个问题的一般答案,而且我的 Google-Fu 到目前为止让我失望了。

在 Promise 的 Catch 处理程序中,如何重新抛出错误,同时仍保留原始错误的 Promise 堆栈跟踪?

这可能不是正确的描述,所以这里是一个例子:

https://jsfiddle.net/8sgj8x4L/19/

使用此代码,跟踪堆栈为:

Warning: a promise was rejected with a non-error: [object String]
    at RejectWithAnError (https://fiddle.jshell.net/_display/:51:19)
    at https://fiddle.jshell.net/_display/:57:14
From previous event:
    at StartTheProgram (https://fiddle.jshell.net/_display/:56:6)
    at window.onload (https://fiddle.jshell.net/_display/:67:1)
bluebird.js:1444 Unhandled rejection an error occurred

但是,如果添加了 catch 处理程序,并且从该处理程序重新拒绝或重新抛出错误,则堆栈将成为新的 Reject 方法调用的位置:

https://jsfiddle.net/8sgj8x4L/18/

哪个跟踪此堆栈跟踪:

Warning: a promise was rejected with a non-error: [object String]
    at https://fiddle.jshell.net/_display/:65:23
From previous event:
    at StartTheProgram (https://fiddle.jshell.net/_display/:61:11)
    at window.onload (https://fiddle.jshell.net/_display/:70:1)
bluebird.js:1444 Unhandled rejection an error occurred

您可以看到调度原始错误“RejectWithAnError”的内部方法从第二个堆栈中消失,因为错误被捕获并重新抛出。

作为参考,这里是来自 JSFiddle 的完整代码(最新的 Bluebird 被引用为外部依赖项):

window.P.longStackTraces();

function RejectWithAnError() {
        var err = {error: true, message: "an error occurred"};
        err.prototype = new Error();
        return window.P.reject(err);
}

function StartTheProgram() {
    return RejectWithAnError()

    // Comment out this catch handler completely, and the Promise stack trace will correctly show the "RejectWithAnError" method as the error origin.
    .catch(function (status) {
            console.log("[WARN] Catch handler was called.");
            // Neither of these lines will show "RejectWithAnError" in the Promise chain stack trace.
            // throw status;
            return window.P.reject(status);

    });
}

StartTheProgram()

(顺便说一句,这是我的第一个 Stack Overflow 问题,所以我希望这是这个问题的正确格式。)

编辑:更新示例以拒绝使用继承自新 Error 实例的对象实例。

【问题讨论】:

  • 旁注:总是拒绝 Error 构造函数的实例。
  • status 是一个字符串,不能保存堆栈信息。这就是为什么您应该始终使用 Error 对象。
  • 感谢您的指点。我已经更新了 JSFiddle 以使用错误实例:https://jsfiddle.net/8sgj8x4L/12/ 原始堆栈跟踪绝对有帮助,理想情况下,它会保留通过复杂的承诺链导致错误的承诺链。例如,在 Bluebird 中启用长堆栈跟踪时,您可以看到导致失败的所有承诺,但如果我添加一个 catch 处理程序,它似乎会在 Promise 中调用该 catch 处理程序之前丢失任何信息链。

标签: javascript stack promise try-catch bluebird


【解决方案1】:

JavaScript 中的错误会在创建时捕获它们的跟踪,因此无论何时重新抛出错误,它都会自动捕获堆栈跟踪。

但是,您并没有抛出错误。出于这个原因,蓝鸟给你一个警告(耶我们)。如果您坚持抛出非错误而不是正确子类化错误 - 您需要通过手动捕获对象来手动欺骗对象以获得正确的堆栈跟踪。通过在构造函数中创建new Error 并将.stack 设置为其堆栈(可能需要进行一些解析)或调用特定方法:

function RejectWithAnError() {
    var err = {error: true, message: "an error occurred"};
    // err.__proto__ = new Error(); -- this is how you'd do it with prototypes btw
    // explicitly capture the stack trace of the object
    if(Error.captureStackTrace) Error.captureStackTrace(err);
}

小提琴here。请注意,.prototype 是一个函数使用的属性,用于指示通过将函数作为构造函数调用而创建的对象的原型。为了直接设置对象的原型,您可以调用__proto__,尽管这不是一个特别好的主意。这是__proto__ instead of Error.captureStackTrace 的示例。

【讨论】:

  • 感谢您的回复。我认为您的意见对我来说很重要的一点是,我从错误的角度看待问题。如果我想查看 Bluebird 打印的完整调试堆栈跟踪,回到启动当前拒绝链的第一个 Reject 的原始位置,这可能是有问题的; (实际上这似乎是 Bluebird 必须提供的东西,相当于 Error.captureStackTrace(err);,但对于异步调用堆栈,例如一个名为 Bluebird.captureAsyncStackTrace(err); 之类的方法)
  • (接上一条评论,似乎有太多字符无法放入单个评论。)但是,如果提供给拒绝调用的对象始终是 Error 的实例,那么在 Promise 链中该点之后的任何catch 处理程序中都可以知道发送错误的原始文件位置,并且一旦知道该位置,那么如果 Bluebird 打印的未捕获异常跟踪是完整的,那么它可能不是很重要。再次,非常感谢您的意见,这绝对是有帮助的。
  • +1,但您能否演示一下使用Object.setPrototypeOf 而不是已弃用的__proto__
  • 它没有被弃用——它是标准的@Bergi
  • @BenjaminGruenbaum:两者都被setPrototypeOf 弃用,并且标准化了(向后)兼容性(查看附件B 介绍)。我认为它已经过时了——它不应该出现在新代码中。
【解决方案2】:
Promise.config({
    warnings: false
});

【讨论】:

    猜你喜欢
    • 2020-11-14
    • 2018-09-19
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-06
    • 2016-06-21
    相关资源
    最近更新 更多