如何正确地打破承诺链?

时间:2015-04-07 19:21:52

标签: jquery promise break chain

基于此处的问题:jQuery chaining and cascading then's and when's和接受的答案。

我想在某个时刻打破承诺链,但还没有找到正确的方法。这有multiple posts about,但我仍然输了。

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

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

3 个答案:

答案 0 :(得分:19)

听起来像want to branchnot to break - 您希望像往常一样继续done。承诺的一个很好的属性是它们不仅链,而且可以nested and unnested没有限制。在您的情况下,您可以在您的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
});

答案 1 :(得分:14)

首先,我认为最好说你正在寻求绕过" (承诺链的一部分)而不是" break"它。

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

另一种机制是使用promise rejection来控制流,然后在链中稍后重新检测特定的错误条件,并将其放回成功路径。

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

答案 2 :(得分:8)

对于使用内置浏览器承诺并寻找停止承诺链的方法而不让所有消费者了解拒绝情况的人,触发任何链式thencatch es或抛出任何Uncaught (in promise)错误,您可以使用以下内容:

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-typing来确定某些东西是否是承诺的事实,所以YMMV(我在Chrome 57.0.2987.98中对此进行了测试),但如果这成为一个问题,你可能会创建一个实际的promise实例和中性它的当时和捕捉方法。