我很难理解为什么拒绝不能通过承诺链传递,我希望有人能帮助我理解其中的原因。对我来说,将功能附加到一系列的承诺中意味着我依赖于最初的承诺来实现它的意图。很难解释,所以让我先给出一个问题的代码示例。(注意:此示例使用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好吧,对我来说,这个结果没有意义。通过附加到这个承诺链上,每个承诺链都意味着它将依赖于d1的成功解决并将结果传递到该链中。如果promise1中的承诺没有接收wins值,而是在其错误处理程序中获得一个错误值,那么如何使链中的下一个承诺调用其成功函数?它不可能将有意义的值传递给下一个承诺,因为它本身没有得到一个值。
另一种我可以描述我的想法的方法是:有三个人,约翰,金格和鲍勃。约翰拥有一家小玩具店。姜走进他的店里,要了一袋五颜六色的小玩意儿。他没有库存,所以他向他的分销商发出了一份请求,要求把它们运给他。同时,他给了金格一次改天的机会,说他欠她一袋小工具。Bob发现Ginger正在获取这些小部件,并要求在完成这些小部件后获得蓝色小部件。她同意了,并给了他一张纸条,说她会的。现在,John的分销商在他们的产品中找不到任何小部件,制造商也不再生产这些小部件,所以他们通知John,John反过来告诉Ginger她无法获得这些小部件。鲍勃怎么能从金格那里得到一个蓝色的小部件,而她却没有得到任何东西?
我对这个问题的第三个更现实的观点是这一点。假设我有两个要更新到数据库的值。其中一个依赖于另一个的id,但是直到我已经将它插入数据库并获得结果之后,我才能获得它的id。除此之外,第一个insert依赖于来自数据库的查询。数据库调用返回承诺,用于将两个调用链接到一个序列中。
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失败,它将调用第一个错误函数。但它会把下一个承诺的成功函数称为“成功函数”。虽然这个承诺是期望第一个值的结果,但是它将从它的错误处理程序函数中得到错误消息。
所以,我的问题是,如果我必须测试我的成功函数中的错误,为什么我会有一个错误处理函数?
很抱歉这么长时间。我只是不知道怎么用另一种方式来解释。
更新与校正
(注:我删除了我曾经对一些评论作出的答复。因此,如果有人对我的回应发表评论,他们的评论可能会显得断章取义,因为我已经删除了它。对不起,我正在尽量把这件事缩短。)
谢谢每一个回复的人。首先,我要向大家道歉,因为我的问题写得如此糟糕,尤其是我的伪代码。我有点太咄咄逼人了,试图保持简短。
多亏了Bergi的回答,我想我在我的逻辑中发现了错误。我想我可能忽略了另一个导致我所面临的问题的问题。这可能导致了承诺链的工作方式与我所认为的不同。我仍然在测试我的代码的不同元素,所以我甚至无法形成一个正确的问题来判断我做错了什么。不过,我确实想更新一下你们的最新情况,谢谢你们的帮助。
发布于 2013-05-04 11:58:58
对我来说,这个结果毫无意义。通过附加到这个承诺链上,每个承诺链都意味着它将依赖于d1的成功解决并将结果传递到该链中。
不是的。您所描述的不是一个链,而是将所有回调附加到d1。但是,如果您想使用then链接某些内容,那么promise2的结果取决于promise1的分辨率以及then回调如何处理它。
文档声明:
返回回调结果的新承诺。
.then方法通常是根据许诺/规格 (或更严格的预言/A+1)来看待的。这意味着回调shell返回承诺将被同化为promise2的解析,如果没有成功/错误处理程序,相应的结果将被直接传递给promise2 -因此您可以简单地省略处理程序来传播错误。
但是,如果处理错误,则结果promise2将被视为固定的,并将使用该值来实现。如果您不想这样做,您就必须使用re-**throw** 错误,就像在try-子句中一样。或者,您可以从处理程序返回一个(即将被拒绝的)承诺。不知道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());鲍勃怎么能从金格那里得到一个蓝色的小部件,而她却没有得到任何东西?
他不应该做得到。如果没有错误处理程序,他只会感知到消息(来自分发服务器)来自John)来自Ginger),即不再有小部件。然而,如果Ginger为这种情况设置了一个错误处理程序,如果John或他的分销商没有蓝色的小部件,那么她仍然可以通过从自己的小屋给Bob一个绿色的小部件来实现她的承诺。
要将错误回调转换为元文件,处理程序中的return err就像说:“如果没有小部件,只需告诉他没有剩下的小部件--它就像想要的小部件一样好”。
在数据库情况下,如果db.query失败,它将调用第一个错误函数
…这意味着错误将在那里被处理。如果不这样做,只需省略错误回调。顺便说一句,你的成功回吐不会return他们正在创造的承诺,所以它们似乎是相当无用的。正确的做法是:
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);发布于 2013-05-05 10:55:37
@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 rejected,但大多数其他承诺库都会返回1 x rejected, 2 x resolved (这将导致假设您通过使用其他承诺库获得了这些结果)。
另外,令人困惑的是,其他库的行为更正确。让我解释一下。
在同步世界中,与“拒绝承诺”相对应的是throw。因此,在语义上,同步异步deferred.reject(new Error())等于throw new Error()。在您的示例中,您没有在同步回调中抛出错误,您只是返回它们,因此切换到成功流,错误是成功的值。为了确保拒绝得到进一步通过,您需要重新抛出错误:
function (err) { console.log('promise1 rejected'); throw err; });所以现在的问题是,为什么延迟库将返回的错误作为拒绝?
原因是,在推迟的情况下,拒绝的工作方式有点不同。在延迟库中,规则是:承诺在使用错误实例进行解析时被拒绝,因此即使您执行deferred.resolve(new Error()),它也将充当deferred.reject(new Error());如果您尝试执行deferred.reject(notAnError),它将抛出一个异常,也就是说,只能在错误实例中拒绝该承诺。这说明了为什么then回调返回的错误拒绝承诺。
递延逻辑背后有一些合理的推理,但它仍然与throw在JavaScript中的工作方式相去甚远,而且由于这种行为被排定为延迟版本v0.7的更改。
摘要:
为了避免混乱和意想不到的结果,只需遵循良好的实践规则:
遵循以上所述,您将在延迟库和其他流行的承诺库中获得一致和预期的结果。
发布于 2016-03-14 11:51:24
Use可以将错误封装在承诺的每个级别上。我将TraceError中的错误链接起来
class TraceError extends Error {
constructor(message, ...causes) {
super(message);
const stack = Object.getOwnPropertyDescriptor(this, 'stack');
Object.defineProperty(this, 'stack', {
get: () => {
const stacktrace = stack.get.call(this);
let causeStacktrace = '';
for (const cause of causes) {
if (cause.sourceStack) { // trigger lookup
causeStacktrace += `\n${cause.sourceStack}`;
} else if (cause instanceof Error) {
causeStacktrace += `\n${cause.stack}`;
} else {
try {
const json = JSON.stringify(cause, null, 2);
causeStacktrace += `\n${json.split('\n').join('\n ')}`;
} catch (e) {
causeStacktrace += `\n${cause}`;
// ignore
}
}
}
causeStacktrace = causeStacktrace.split('\n').join('\n ');
return stacktrace + causeStacktrace;
}
});
// access first error
Object.defineProperty(this, 'cause', {value: () => causes[0], enumerable: false, writable: false});
// untested; access cause stack with error.causes()
Object.defineProperty(this, 'causes', {value: () => causes, enumerable: false, writable: false});
}
}使用
throw new TraceError('Could not set status', srcError, ...otherErrors);输出

函数
TraceError#cause - first error
TraceError#causes - list of chained errorshttps://stackoverflow.com/questions/16371129
复制相似问题