ES6承诺错误没有像预期的那样冒泡

时间:2016-07-28 00:19:51

标签: javascript error-handling promise ecmascript-6

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

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

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

它非常简单,只返回传递给它的字符串的promise,并导致在“timeout”毫秒内解析或拒绝该promise(基于第三个参数)。

我可以完全按照以下方式使用它:

            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));

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

  

DoublePromise:BigPromise

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

            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的链末尾添加一个catch,我只能得到我所期望的(和欲望),如下所示:

    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
        });
    }

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

我是否错过了一种简单的工作方式?

我错过了一些基本概念吗?

1 个答案:

答案 0 :(得分:4)

您正在使用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()处理程序中返回内部承诺,因为这样可以使嵌套错误向上传播,也可以正确地链/序列异步操作。

并且,您希望避免在现有承诺中包含新承诺,因为您可以直接链接到/和/或返回您已有的承诺。

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

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

很乐意做两个console.log()语句,因为.catch()"处理了"承诺让承诺链继续愉快。

正如我在之前的评论中所说,这些100%的理论讨论很难得到你真正想要完成的事情(我们必须猜出真正的问题是什么),而没有关于承诺如何工作的20页教程很多东西。如果你发布一个你试图用这种技术解决的现实世界的问题会更好,我们可以用几行代码和几段解释来展示/解释这样做的最佳方法