如何正确地打破承诺链?

Den*_*s G 32 jquery break promise chain

基于这里的问题: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
});
Run Code Online (Sandbox Code Playgroud)

如果我打破链条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
});
Run Code Online (Sandbox Code Playgroud)

Ber*_*rgi 20

听起来你分支,而不是打破 - 你想像往常一样继续done.承诺的一个很好的属性是它们不仅可以链接,而且可以无限制嵌套和取消.在您的情况下,您可以将链条的一部分放在您的if-statement中"打破" :

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
});
Run Code Online (Sandbox Code Playgroud)

  • 这看起来正是Promise所要避免的,回调地狱 (5认同)
  • 这是正确的解决方案; 它完全匹配同步版本而不会滥用承诺拒绝.(你不会通过`throw`一个任意错误来突破内部循环只是为了'抓住`它在外面,对吗?)外部`then`返回的内部承诺可以重构为它自己的函数,就像任何其他Javascript重构器. (3认同)
  • @DennisG:你需要移动那些需要"吃饭"的`then`.它应该不再遵循`getCantinas().then(...)`,但它应该嵌套在回调中.我希望缩进能说清楚 (2认同)
  • @Yerken:我在这里看不到任何回调地狱.所有这些函数都"返回"有用的东西. (2认同)

Roa*_*888 14

首先,我认为最好是说你试图"绕过"(部分)承诺链而不是"打破"它.

正如你所说,在几个地方测试"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'
};
Run Code Online (Sandbox Code Playgroud)

从好的方面来说,我发现缺乏嵌套使得自然成功路径的可读性更好.此外,至少对我来说,这种模式需要最少的精神杂耍,以便在需要时容纳更多的绕道.

在不利方面,这种模式的效率略低于Bergi.虽然主路径具有与Bergi相同数量的承诺,但cantinas.length == 0路径需要一个(如果编码了多个旁路,则每个旁路一个).此外,这种模式需要可靠地重新检测特定的错误条件 - 因此errMessages对象 - 有些人可能会发现这些错误.


jst*_*aab 9

对于使用内置浏览器承诺并寻找停止承诺链的方法而不让所有消费者知道拒绝情况,触发任何链接thencatches或抛出任何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
Run Code Online (Sandbox Code Playgroud)

基本上,noopPromise是一个基本的存根承诺接口,它接受链接函数,但从不执行任何操作.这依赖于显然浏览器使用duck-typing来确定某些东西是否是承诺的事实,所以YMMV(我在Chrome 57.0.2987.98中对此进行了测试),但如果这成为一个问题,你可能会创建一个实际的promise实例和中性它的当时和捕捉方法.