从原始问题中获取示例代码:
Menus.getCantinas().then(function(cantinas){ // `then` is how we chain promises
Menus.cantinas = cantinas;
// if we need to aggregate more than one promise, we `$.when`
return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas));
}).then(function(meals, sides){ // in jQuery `then` can take multiple arguments
Menus.sides = sides; // we can fill closure arguments here
Menus.meals = meals;
return Menus.getAdditives(meals, sides); // again we chain
}).then(function(additives){
Menus.additives = additives;
return Menus; // we can also return non promises and chain on them if we want
}).done(function(){ // done terminates a chain generally.
// edit HTML here
});
我将如何如果
cantinas.length == 0
打破链条?坦白说,我不想吃顿饭,也不想添加餐食,我想称呼某种“空结果”回调。我尝试了以下非常丑陋的方法(但是可以工作...)。教我正确的方法。这仍然是有效的结果,因此本质上不是“失败”,我只是说空的结果。var emptyResult = false;
Menus.getCantinas().then(function(cantinas){
Menus.cantinas = cantinas;
if (cantinas.length == 0) {
emptyResult = true;
return "emptyResult"; //unuglify me
}
return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas));
}).then(function(meals, sides){
if (meals == "emptyResult") return meals; //look at my ugliness...
Menus.sides = sides;
Menus.meals = meals;
return Menus.getAdditives(meals, sides);
}).then(function(additives){
if (additives == "emptyResult") return additives;
Menus.additives = additives;
return Menus;
}).done(function(){
if (emptyResult)
//do empty result stuff
else
// normal stuff
});
#1 楼
首先,我认为最好说您是在“绕过”承诺链(而不是“打破”)。正如您所说,在多个地方测试“ emptyResult”非常难看。幸运的是,在遵循不执行某些promise链的相同一般原则的同时,还可以使用更优雅的机制。
另一种机制是使用承诺拒绝来控制流程,然后在链中稍后重新检测特定的错误情况,然后将其放回成功路径。 >
Menus.getCantinas().then(function(cantinas) {
Menus.cantinas = cantinas;
if(cantinas.length == 0) {
return $.Deferred().reject(errMessages.noCantinas);
} else {
return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas));
}
}).then(function(meals, sides) {
Menus.sides = sides;
Menus.meals = meals;
return Menus.getAdditives(meals, sides);
}).then(function(additives) {
Menus.additives = additives;
return Menus;
}).then(null, function(err) {
//This "catch" exists solely to detect the noCantinas condition
//and put the chain back on the success path.
//Any genuine error will be propagated as such.
//Note: you will probably want a bit of safety here as err may not be passed and may not be a string.
return (err == errMessages.noCantinas) ? $.when(Menus) : err;
}).done(function(Menus) {
// with no cantinas, or with everything
});
var errMessages = {
'noCantinas': 'no cantinas'
};
从好的方面来说,我发现缺少嵌套可以更好地读取自然成功路径。另外,至少对我来说,如果需要的话,这种模式将需要最少的心理操作来适应更多的绕行。
从不利的方面来说,这种模式的效率比Bergi的效率稍低。主路径的承诺数量与Bergi的数量相同,而
cantinas.length == 0
路径则需要更多数量(如果编码了多个旁路,则每个旁路需要一个)。同样,此模式要求可靠地重新检测特定的错误情况,因此errMessages
对象可能会有所损益。评论
尽管效率很高,但实际上我更喜欢这种模式。我实际上认为错误对象是有道理的,因为正如您所说,可能还有其他情况。然后返回$ .Deferred()。reject(errMessages.noCantinas);在我眼中是美丽的,尽管这不是“真正的”拒绝,但这没关系。
–丹尼斯G
15年4月8日在7:56
我真的应该对return $ .Deferred()。reject(errMessages.noCantinas)发表评论; -类似于// bypass-rejection。令人放心的是,您无需注释就可以理解模式。
–漫游者1888
15年4月8日在8:42
#2 楼
听起来您想分支而不是中断-您想像往常一样继续到done
。 promise的一个很好的特性是它们不仅可以链接,而且可以无限制地嵌套和取消嵌套。就您而言,您只需将要“断开”的链的一部分放在if
-语句中:Menus.getCantinas().then(function(cantinas) {
Menus.cantinas = cantinas;
if (cantinas.length == 0)
return Menus; // break!
// else
return $.when(Menus.getMeals(cantinas), Menus.getSides(cantinas))
.then(function(meals, sides) {
Menus.sides = sides;
Menus.meals = meals;
return Menus.getAdditives(meals, sides);
}).then(function(additives) {
Menus.additives = additives;
return Menus;
});
}).done(function(Menus) {
// with no cantinas, or with everything
});
评论
听起来像是正确的方法,但是当返回菜单时,仍然会进行以下所有调用(即.then(function(meals,两侧)...然后then(function(additives))。)没有小餐馆(第一次呼叫时为空),当返回Menus时,进餐==以下菜单。
–丹尼斯G
2015年4月7日在20:15
@DennisG:您需要移动那顿饭。它不应再跟随getCantinas()。then(…),而应嵌套在回调中。我希望缩进可以清楚地说明
–贝尔吉
2015年4月7日在20:20
这看起来就像是要避免的承诺,回调地狱
–耶肯
16-2-14在6:04
@Yerken:我在这里看不到任何回调地狱。所有这些函数都返回有用的东西。
–贝尔吉
16年2月14日在12:09
这是正确的解决方案。它与同步版本完全匹配,而不会滥用承诺拒绝。 (您不会通过抛出一个任意错误来将其捕获到外部来打破内部循环,对吗?)可以像其他任何Javascript重构一样将内部然后返回的内部承诺重构为自己的函数。
–杰夫·鲍曼(Jeff Bowman)
16年7月29日在4:58
#3 楼
对于使用内置浏览器promise的人们,并寻找一种在不让所有消费者都知道拒绝情况,触发任何链接的then
或catch
es或引发任何Uncaught (in promise)
错误的情况下,终止承诺链的方法,可以使用以下方法:基本上,noopPromise是一个基本的承诺接口,它具有链接功能,但从不执行任何功能。这是基于这样一个事实,即浏览器显然使用鸭式输入来确定某个东西是否是一个承诺,因此使用YMMV(我在Chrome 57.0.2987.98中对此进行了测试),但是如果这成为问题,则可以创建一个实际的promise实例和中性对象其then和catch方法。评论
很好的答案,谢谢。它也可以在节点上工作-我们的团队依靠V8中的这种鸭嘴行为与等待Waterline项目相结合。为了模拟早期收益,我们已经开始尝试一些更复杂的选择,但这是一个很好的线索!今晚我会调查一下。如果我发现任何问题(例如内存泄漏),我会尽力记得进行报告,但是如果您或其他任何人感到好奇并且我没有跟进,请随时在这里ping我作为提醒。
– mikermcneil
17-10-14在23:08
这是一个很好的解决方案。比伪造错误或嵌套承诺链要好得多。谢谢!
–CaptRespect
'18 -10-1在20:31
评论
另请参阅跳过Q Promises中的then函数的正确方法