拒绝不会在链式承诺中传播
P粉193307465
P粉193307465 2023-10-23 17:50:47
[JavaScript讨论组]

我无法理解为什么拒绝不通过承诺链传递,我希望有人能够帮助我理解原因。对我来说,将功能附加到一系列承诺意味着我依赖于要履行的原始承诺的意图。这很难解释,所以让我先展示我的问题的代码示例。 (注意:本示例使用 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粉193307465
P粉193307465

全部回复(1)
P粉155710425

@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 库中获得一致且预期的结果。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号