【问题标题】:ES6 Promise Errors not bubbling up as expectedES6 Promise 错误没有按预期冒泡
【发布时间】:2016-12-02 04:29:34
【问题描述】:

我从 E6 Promises 开始。我非常喜欢它们,但有一个关于错误处理的关键概念,我不明白,希望得到澄清。

让我们假设以下简单的函数返回一个承诺:

    function promiseString(str, timeout, doResolve) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (doResolve) {
                    resolve(str);
                } else {
                    reject(new Error("Rejecting " + str));
                }
            }, timeout);
        });
    }

这很简单,只是为传递给它的字符串返回一个承诺,并导致该承诺在“超时”毫秒内被解析或拒绝(基于第三个参数)。

我可以完全按照预期使用它,如下所示:

            promiseString("One", 100, true)
                .then((str) => { console.log("First then is " + str); return promiseString(str + " two", 100, true); })
                .then((str) => { console.log("Second then is " + str); return promiseString(str + " three", 100, true); })
                .then((str) => console.log(str))
                .catch((err) => console.error(err));

如果在此链中的任何调用中将第三个参数从“true”更改为“false”,我的错误将按预期被捕获并发送到 console.error()。

但是,现在想象一下用于构造有前途的对象的以下(同样愚蠢的)函数:

    function DoublePromiser(str1, str2, doResolve) {
        this.promise = new Promise((resolve, reject) => {
            promiseString(str1, 100, doResolve)
                .then((s1) => promiseString(s1 + str2, 100, doResolve))
                .then((s2) => resolve(s2));
        });
    }

现在想象一下,我按如下方式使用这段代码,一切都解决了,没有任何拒绝,(doResolve 设置为 true):

            var dp = new DoublePromiser("Big", "Promise", true);
            dp.promise
                .then((s) => console.log("DoublePromise: " + s))
                .catch((err)=>console.log("I did catch: ", err.message));

正如所料,我在控制台中看到以下内容:

双重承诺:大承诺

但是,现在我更改了消费代码,将 doResolve 设置为“false”(这会导致我的 promise 例程被拒绝):

            var dp = new DoublePromiser("Big", "Promise", false);
            dp.promise
                .then((s) => console.log("DoublePromise: " + s))
                .catch((err)=>console.log("I did catch: ", err.message));

由于我对错误应该如何“冒泡”的理解,我希望控制台记录如下:

我确实抓住了:拒绝大

但事实并非如此。相反,控制台显示一个未捕获的错误:

未捕获(承诺)错误:拒绝大

如果我在 DoublePromiser 的链末尾添加一个捕获,我只会得到我期望(和渴望)的结果,如下所示:

    function DoublePromiser(str1, str2, doResolve) {
        this.promise = new Promise((resolve, reject) => {
            promiseString(str1, 100, doResolve)
                .then((s1) => promiseString(s1 + str2, 100, doResolve))
                .then((s2) => resolve(s2))
                .catch((err) => reject(err)); // ADDING THIS TO MAKE IT WORK
        });
    }

现在我得到了我的期望,错误并非没有被发现。但这似乎与错误冒泡的整个想法背道而驰,并且为了重新拒绝相同的错误而捕获错误似乎很奇怪。

我是否错过了一种让它简单地工作的方法?

我是否遗漏了一些基本概念?

【问题讨论】:

  • 这些 100% 的理论问题使用虚构的代码,并没有说明真正的问题,只是在堆栈溢出问题上效果不佳。
  • 另外,您正在使用 promise 构造函数反模式:github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns
  • @estus - 我编辑了他们的标题。
  • @jfriend00 谢谢。顺便说一句,很好的答案,它确定了这一点。
  • 重新打开,因为尽管其他答案包含可用于回答此问题的信息,但它们甚至与同一个实际问题都不接近。读过其他标题或问题的人不会认为这与这个问题是同一个问题。

标签: javascript error-handling promise ecmascript-6


【解决方案1】:

您正在使用 Promise 构造函数反模式。不要将现有的 Promise 包装在你自己做出的另一个 Promise 中,因为这只会让你做很多额外的工作来让事情正常工作,而且由于大多数人没有正确地做这些额外的工作,它也很容易出现编程错误。只是回报你已经拥有的承诺。

改变这个:

function DoublePromiser(str1, str2, doResolve) {
    this.promise = new Promise((resolve, reject) => {
        promiseString(str1, 100, doResolve)
            .then((s1) => promiseString(s1 + str2, 100, doResolve))
            .then((s2) => resolve(s2))
            .catch((err) => reject(err)); // ADDING THIS TO MAKE IT WORK
    });
}

到这里:

function DoublePromiser(str1, str2, doResolve) {
    return promiseString(str1, 100, doResolve)
       .then((s1) => promiseString(s1 + str2, 100, doResolve));
}

然后,只需将其用作函数:

DoublePromiser("Big", "Promise", false).then(...);

回顾:您几乎总是希望从 .then() 处理程序中返回内部承诺,因为这允许嵌套错误向上传播并正确链接/序列异步操作。

而且,您几乎总是希望避免将新的 Promise 包装在现有的 Promise 周围,因为您可以链接到和/或返回您已经拥有的现有 Promise。

另外,请注意,如果您执行 .catch(),它将“处理”被拒绝的承诺并返回新的未拒绝承诺,从那里继续承诺链,除非在 .catch() 处理程序中您返回被拒绝承诺或抛出异常。所以,这个:

p.catch((err) => console.log(err)).then(() => console.log("chain continues"))

很乐意同时执行console.log() 语句,因为.catch()“处理”了承诺,因此承诺链愉快地继续。


正如我在之前的 cmets 中所说,如果没有 20 页的关于 Promise 如何工作的教程,这些 100% 的理论讨论很难准确地得出您真正想要完成的目标(我们必须猜测真正的问题是什么)很多东西。如果你发布一个你试图用这种技术解决的现实世界的问题,那就更好了,我们可以用几行代码和几段解释来展示/解释最好的方法。

【讨论】:

  • 感谢您在这方面所做的工作。明天我会根据你的答案工作。其“真实世界”版本是如此庞大和怪异,很难想象对 StackOverflow 进行简洁的描述。它与使用 IndexedDb 为 SAAS 系统创建缓存系统有关,但缓存系统非常复杂。我花了相当长的时间将“真实世界”的问题归结为这个“虚假世界”的摘录。我们当前的设计确实要求我们通过构造函数创建一个对象,但我会尝试使用你的概念而不是下一个承诺,看看我会得到什么。
  • 而且.... 这就是我喜欢 StackOverflow 的原因。在阅读了许多规范文档和教程并“大部分”正确之后,一分钱终于落到了“反模式”和嵌套上。在构造函数中做同样的事情也很好。完美的。谢谢。
  • @StephanGolux - 太棒了。构造函数的问题是它不能返回一个承诺。但是,你可以做你正在做的事情,即在实例数据中坚持承诺,并在调用构造函数后伸手去拿它。虽然这对我来说似乎很奇怪,但也许是因为你没有透露任何其他用途,让它成为一个对象/构造函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
  • 1970-01-01
  • 2019-11-04
  • 1970-01-01
相关资源
最近更新 更多