如何正确摆脱承诺链?

基于这里的问题: 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 }); 

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

正如你所说,在几个地方测试“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 cantinas.length == 0路径需要多一个(如果绕过多个编码,则每个旁路需要一个)。 此外,这种模式需要可靠的重新检测特定的错误条件 – 因此errMessages对象 – 有些人可能会发现有损。

听起来像你想分支 ,而不是打破 – 你想继续照常done 。 承诺的一个不错的属性是它们不仅可以链接,而且可以无限制地嵌套和放置 。 在你的情况下,你可以把你想要在你的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 }); 

对于使用内置的浏览器承诺的人来说,如果不让所有消费者都知道拒绝的情况,触发任何链接的或者catch或者抛出任何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是一个基本的stubed out承诺接口,它使用链接函数,但从不执行任何操作。 这依赖于这样一个事实,显然浏览器使用鸭子打字来确定是否有承诺,所以YMMV(我在Chrome 57.0.2987.98中测试过),但如果这成为一个问题,你可能会创建一个实际的承诺实例和neuter其当时的捕捉方法。