基于这里的问题:jQuery链式和级联,然后是什么时候,以及已接受的答案,我想在某个时候打破承诺链,但还没有找到正确的方法。关于此有很多帖子,但我仍然迷失了。

从原始问题中获取示例代码:

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
});


评论

另请参阅跳过Q Promises中的then函数的正确方法

#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的人们,并寻找一种在不让所有消费者都知道拒绝情况,触发任何链接的thencatch 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