Jquery 如何恰当地打破承诺链?

Jquery 如何恰当地打破承诺链?,jquery,promise,break,chain,Jquery,Promise,Break,Chain,基于这里的问题:和公认的答案,我想在某个点上打破承诺链,但还没有找到正确的方法。有这个,但我还是迷路了 以原始问题中的示例代码为例: 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` re

基于这里的问题:和公认的答案,我想在某个点上打破承诺链,但还没有找到正确的方法。有这个,但我还是迷路了

以原始问题中的示例代码为例:

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
});
听起来像你,-你想像往常一样继续
完成
。承诺的一个很好的特性是,它们不仅可以连锁,而且可以不受限制。在您的情况下,您可以将要“断开”的链的一部分放在
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
});

首先,我认为最好说你正在寻求“绕过”(部分)承诺链,而不是“打破”它

正如你所说,在几个地方测试“emptyResult”是非常难看的。幸运的是,在遵守不执行某些承诺链的相同一般原则的同时,可以使用更优雅的机制

另一种机制是使用承诺拒绝来控制流,然后在链的后面重新检测特定的错误条件,并将其放回成功路径

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
对象,有些人可能会发现这一点。

对于使用内置浏览器承诺的人,如果他们想在不让所有消费者知道拒绝情况的情况下停止承诺链,触发任何链接的
然后
捕获
es或抛出任何
未捕获(承诺)
错误,您可以使用以下方法:

var noopPromise = {
  then: () => noopPromise, 
  catch: () => noopPromise
}

function haltPromiseChain(promise) {
  promise.catch(noop)

  return noopPromise
}

// Use it thus:
var p = Promise.reject("some error")
p = haltPromiseChain(p)
p.catch(e => console.log(e)) // this never happens

基本上,noopPromise是一个基本的存根承诺接口,它接受链接函数,但从不执行任何函数。这取决于这样一个事实,即浏览器显然使用duck类型来确定某个东西是否是承诺,因此YMMV(我在Chrome 57.0.2987.98中测试了这个),但如果这成为一个问题,您可能会创建一个实际的承诺实例,并对其then和catch方法进行中性化。

听起来是正确的方法,但当返回
菜单时
仍在进行以下所有调用(即
。然后(功能(膳食、配餐)
然后(功能(添加剂)
)。不应进行这些调用,因为没有
餐厅
(第一次调用为空)。当返回
菜单时,
餐点
==
菜单
在下面的
然后
@DennisG:你需要移动
然后
餐点
。它不应该再跟随
getCantinas()。然后(…)
,但它应该嵌套在回调中。我希望缩进能让它变得清晰,这看起来正是承诺要避免的回调hell@Yerken:我在这里没有看到任何回调地狱。所有这些函数
都返回一些有用的东西。这是正确的解决方案;它与同步版本完全匹配不要误用承诺拒绝。(你不会通过抛出一个任意错误而将其捕获到外部,从而打破一个内部循环,对吗?)内部承诺,外部
then
返回可以重构为自己的函数,就像任何其他Javascript重构一样。尽管效率很高,但我实际上更喜欢这种模式。我实际上相信错误对象是有意义的,因为正如您所说,可能还有其他情况。而这个
return$.Deferred().reject(errMessages.noCantinas);
在我看来很漂亮,虽然这不是一个“真正的”拒绝,但没关系。我真的应该对
return$.Deferred().reject(errMessages.noCantinas)发表评论
-类似于
//绕过拒绝
。您可以放心地理解该模式,而无需注释。另请参阅极好的答案,谢谢。它也可以在node中工作——我们的团队在V8中依赖此ducktyping行为,并与Waterline项目的
wait
相结合。为了模拟早期回报,我们已经启动了e正在尝试一些更复杂的选项,但这是一个很好的线索!今晚我将对此进行研究。如果我发现任何问题(例如内存泄漏),我会尽量记得向你汇报,但如果你或其他人好奇,而我没有跟进,请随时在这里提醒我。这是一个很好的解决方案。比假装错误或嵌套承诺链要好得多。谢谢!