我无法理解为什么拒绝不通过承诺链传递,我希望有人能够帮助我理解原因。对我来说,将功能附加到一系列承诺意味着我依赖于要履行的原始承诺的意图。这很难解释,所以让我先展示我的问题的代码示例。 (注意:本示例使用 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粉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 版本中进行更改。
简短摘要:
为了避免混乱和意外结果,只需遵循良好实践规则:
遵守上述规定,您将在延迟库和其他流行的 Promise 库中获得一致且预期的结果。
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);