首页  >  问答  >  正文

拒绝不会在链式承诺中传播

我无法理解为什么拒绝不通过承诺链传递,我希望有人能够帮助我理解原因。对我来说,将功能附加到一系列承诺意味着我依赖于要履行的原始承诺的意图。这很难解释,所以让我先展示我的问题的代码示例。 (注意:本示例使用 Node 和延迟节点模块。我使用 Dojo 1.8.3 对此进行了测试,并得到了相同的结果)

var d = require("deferred");

var d1 = d();

var promise1 = d1.promise.then(
    function(wins) { console.log('promise1 resolved'); return wins;},
    function(err) { console.log('promise1 rejected'); return err;});
var promise2 = promise1.then(
    function(wins) { console.log('promise2 resolved'); return wins;},
    function(err) { console.log('promise2 rejected'); return err;});
var promise3 = promise2.then(
    function(wins) { console.log('promise3 resolved'); return wins;},
    function(err) { console.log('promise3 rejected'); return err;});
d1.reject(new Error());

运行此操作的结果是这样的输出:

promise1 rejected
promise2 resolved
promise3 resolved

好吧,对我来说,这个结果没有意义。通过附加到这个 Promise 链,每个 then 都暗示着这样的意图:它将依赖于 d1 的成功解析以及沿着链传递的结果。如果promise1中的promise没有收到wins值,而是在其错误处理程序中收到一个err值,那么链中的下一个promise怎么可能调用它的success函数呢?它无法将有意义的值传递给下一个 Promise,因为它本身没有获得值。

我可以用另一种方式来描述我的想法:有三个人:John、Ginger 和 Bob。约翰拥有一家小部件商店。金杰走进他的店里,要了一袋各种颜色的小部件。他没有库存,因此他向经销商发送请求,要求将它们运送给他。与此同时,他给了金杰一张雨支票,说他欠她那袋小部件。鲍勃发现金杰正在获取这些小部件,并要求他在她用完这些小部件后获取蓝色的小部件。她同意了,并给了他一张纸条,表示她会同意。现在,约翰的经销商在他们的供应中找不到任何小部件,并且制造商不再生产这些小部件,因此他们通知约翰,约翰又通知金杰她无法获得这些小部件。当 Bob 自己没有得到任何东西时,他如何能够从 Ginger 那里得到蓝色小部件?

我对这个问题的第三个更现实的观点是这样的。假设我有两个值想要更新到数据库。一个依赖于另一个的 id,但在我将其插入数据库并获得结果之前,我无法获取 id。最重要的是,第一次插入取决于数据库的查询。数据库调用返回的承诺是我用来将两个调用链接成一个序列的。

var promise = db.query({parent_id: value});
promise.then(function(query_result) {
    var first_value = {
        parent_id: query_result[0].parent_id
    }
    var promise = db.put(first_value);
    promise.then(function(first_value_result) {
        var second_value = {
            reference_to_first_value_id: first_value_result.id
        }
        var promise = db.put(second_value);
        promise.then(function(second_value_result) {
            values_successfully_entered();
        }, function(err) { return err });
    }, function(err) { return err });
}, function(err) { return err });

现在,在这种情况下,如果 db.query 失败,它将调用第一个 then 的 err 函数。但随后它会调用下一个承诺的成功函数。虽然该 Promise 期望第一个值的结果,但它会从其错误处理函数获取错误消息。

所以,我的问题是,如果我必须测试成功函数中的错误,为什么还要有错误处理函数?

抱歉,这篇文章太长了。我只是不知道如何用另一种方式解释它。

更新和更正

(注意:我删除了我曾经对某些评论做出的回复。因此,如果有人对我的回复发表评论,那么既然我删除了它,他们的评论可能会显得断章取义。对此表示抱歉,我试图将其保留为尽可能短。)

谢谢大家的回复。我首先想向大家道歉,因为我的问题写得这么差,尤其是我的伪代码。我有点过于激进地试图保持简短。

感谢 Bergi 的回复,我想我发现了我的逻辑错误。我想我可能忽略了导致我遇到问题的另一个问题。这可能导致承诺链的工作方式与我想象的不同。我仍在测试代码的不同元素,所以我什至无法形成一个正确的问题来看看我做错了什么。不过,我确实想向大家通报最新情况,并感谢你们的帮助。

P粉193307465P粉193307465362 天前2827

全部回复(2)我来回复

  • P粉155710425

    P粉1557104252023-10-24 16:07:16

    @Jordan 首先,正如评论者指出的,当使用延迟库时,您的第一个示例肯定会产生您期望的结果:

    promise1 rejected
    promise2 rejected
    promise3 rejected

    其次,即使它会产生您建议的输出,它也不会影响第二个代码段的执行流程,这有点不同,更像是:

    promise.then(function(first_value) {
        console.log('promise1 resolved');
        var promise = db.put(first_value);
        promise.then(function (second_value) {
             console.log('promise2 resolved');
             var promise = db.put(second_value);
             promise.then(
                 function (wins) { console.log('promise3 resolved'); },
                 function (err) { console.log('promise3 rejected'); return err; });
        }, function (err) { console.log('promise2 rejected'); return err;});
    }, function (err) { console.log('promise1 rejected'); return err});

    并且,如果第一个承诺被拒绝,只会输出:

    promise1 rejected

    然而(到达最有趣的部分)即使延迟库肯定返回 3 x returned,大多数其他承诺库将返回 1 x returned, 2 x 已解决(这导致假设您通过使用其他一些 Promise 库获得了这些结果)。

    另外令人困惑的是,其他库的行为更加正确。让我解释一下。

    在同步世界中,“承诺拒绝”的对应部分是抛出。因此从语义上讲,同步中的异步 deferred.reject(new Error()) 等于 throw new Error()。 在您的示例中,您不会在同步回调中​​抛出错误,您只是返回它们,因此您切换到成功流程,其中错误是成功值。为了确保拒绝进一步通过,您需要重新抛出错误:

    function (err) { console.log('promise1 rejected'); throw err; });

    现在的问题是,为什么延迟库将返回的错误视为拒绝?

    原因是延迟工作中的拒绝有点不同。在 deferred lib 中,规则是:当出现错误实例时,promise 会被拒绝,因此即使你执行 deferred.resolve(new Error()) 它也会起作用如 deferred.reject(new Error()) ,如果你尝试执行 deferred.reject(notAnError) ,它会抛出一个异常,表示该 Promise 只能被拒绝有错误的实例。这清楚地表明了为什么从 then 回调返回的错误拒绝了承诺。

    延迟逻辑背后有一些有效的推理,但它仍然与 JavaScript 中 throw 的工作方式不符,因此,此行为计划在延迟的 v0.7 版本中进行更改。

    简短摘要:

    为了避免混乱和意外结果,只需遵循良好实践规则:

    1. 始终拒绝带有错误实例的承诺(遵循同步世界的规则,其中抛出非错误的值被视为不好的做法)。
    2. 通过抛出错误拒绝同步回调(返回错误并不能保证拒绝)。

    遵守上述规定,您将在延迟库和其他流行的 Promise 库中获得一致且预期的结果。

    回复
    0
  • P粉376738875

    P粉3767388752023-10-24 12:09:36

    没有。您所描述的不是一个链,而只是将所有回调附加到d1。然而,如果您想使用 then 链接某些内容,promise2 的结果取决于 promise1 的分辨率以及 promise1 的分辨率以及 < code>然后回调处理了它。

    文档指出:

    .then 方法通常根据 Promises 来看待/A 规范(或者更严格的Promsises/A+ )。这意味着回调 shell 返回的 Promise 将被同化为 Promise2 的解析,如果没有成功/错误处理程序,相应的结果将直接传递给 Promise2 code> - 因此您可以简单地省略处理程序来传播错误。

    但是,如果错误被处理,则生成的 promise2 被视为已修复,并将用该值来实现。如果您不希望出现这种情况,则必须重新抛出错误,就像在 try-catch 子句中一样。或者,您可以从处理程序返回一个(待)拒绝的承诺。不确定 Dojo 的拒绝方式是什么,但是:

    var d1 = d();
    
    var promise1 = d1.promise.then(
        function(wins) { console.log('promise1 resolved'); return wins;},
        function(err) { console.log('promise1 rejected'); throw err;});
    var promise2 = promise1.then(
        function(wins) { console.log('promise2 resolved'); return wins;},
        function(err) { console.log('promise2 rejected'); throw err;});
    var promise3 = promise2.then(
        function(wins) { console.log('promise3 resolved'); return wins;},
        function(err) { console.log('promise3 rejected'); throw err;});
    d1.reject(new Error());

    他不应该能够。如果没有错误处理程序,他只会感知到没有剩余小部件的消息(((来自约翰)来自 Ginger)。然而,如果金格为这种情况设置了一个错误处理程序,如果约翰或他的经销商没有剩下蓝色的小部件,她仍然可以履行她的承诺,从她自己的小屋里给鲍勃一个小部件,给他一个绿色的小部件。

    要将错误回调转换为元,从处理程序返回错误就像说“如果没有留下任何小部件,只需给他一个注释,即没有留下任何小部件 - 这是与所需的小部件一样好”。

    ...这意味着错误已在那里处理。如果您不这样做,只需省略错误回调即可。顺便说一句,您的成功回调不会返回它们正在创建的承诺,因此它们似乎毫无用处。正确的是:

    var promise = db.query({parent_id: value});
    promise.then(function(query_result) {
        var first_value = {
            parent_id: query_result[0].parent_id
        }
        var promise = db.put(first_value);
        return promise.then(function(first_value_result) {
            var second_value = {
                reference_to_first_value_id: first_value_result.id
            }
            var promise = db.put(second_value);
            return promise.then(function(second_value_result) {
                return values_successfully_entered();
            });
        });
    });

    或者,由于您不需要闭包来访问先前回调的结果值,甚至:

    db.query({parent_id: value}).then(function(query_result) {
        return db.put({
            parent_id: query_result[0].parent_id
        });
    }).then(function(first_value_result) {
        return db.put({
            reference_to_first_value_id: first_value_result.id
        });
    }.then(values_successfully_entered);

    回复
    0
  • 取消回复