jQuery 如何正确跳出承诺链?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29499582/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to properly break out of a promise chain?
提问by Dennis G
Based on the question here: jQuery chaining and cascading then's and when'sand the accepted answer, I want to break the promise chain at a point but haven't yet found the correct way. There are multiplepostsaboutthis, but I am still lost.
基于这里的问题:jQuery chaining and cascading then's and when'sand the Accepted answer,我想在某个时候打破承诺链,但还没有找到正确的方法。有多个职位有关这一点,但我还是输了。
Taking the example code from the original question:
从原始问题中获取示例代码:
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
});
How would I break the chain if cantinas.length == 0
? I would not want to get the meals, neither the additives, frankly I would want to call some kind of "empty result" callback. I have tried the following which is very ugly(but works...). Teach me the correct way. This still is a valid result, so not a "fail" per se, just empty result I would say.
如果 ,我将如何打破链条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
});
采纳答案by Roamer-1888
Firstly, I think it better to say you are seeking to "bypass" (part of) the promise chain rather than to "break" it.
首先,我认为最好说您正在寻求“绕过”(部分)承诺链而不是“打破”它。
As you say, testing for "emptyResult" in several places is pretty ugly. Fortunately, a more elegant mechanism is available while adhering to the same general principle of not executing some of the promise chain.
正如您所说,在几个地方测试“emptyResult”非常难看。幸运的是,在遵循不执行某些承诺链的相同一般原则的同时,可以使用更优雅的机制。
An alternative mechanism is to use promise rejection to control the flow, then to re-detect the specific error condition(s) later in the chain, and put it back on the success path.
另一种机制是使用承诺拒绝来控制流程,然后重新检测链中稍后的特定错误条件,并将其放回成功路径。
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'
};
On the plus side, I find the lack of nesting makes for better readability of the natural success path. Also, for me at least, this pattern would require minimal mental juggling to accommodate further bypasses, if needed.
从好的方面来说,我发现没有嵌套可以提高自然成功路径的可读性。此外,至少对我而言,如果需要,这种模式将需要最少的心理杂耍以适应进一步的绕过。
On the down side, this pattern is slightly less efficient than Bergi's. Whereas the main path has the same number of promises as Bergi's, the cantinas.length == 0
path requires one more (or one per bypass if multiple bypasses were coded). Also, this pattern requires reliable re-detection of specific error condition(s) - hence the errMessages
object - which some may find detracts.
不利的一面是,这种模式的效率略低于 Bergi 的模式。尽管主路径与 Bergi 具有相同数量的承诺,但该cantinas.length == 0
路径需要多一个(如果编码了多个旁路,则每个旁路一个)。此外,这种模式需要可靠地重新检测特定的错误条件 - 因此是errMessages
对象 - 有些人可能会发现它有损。
回答by Bergi
Sounds like you want to branch, not to break- you want to continue as usual to the done
. A nice property of promises is that they don't only chain, but also can be nested and unnestedwithout restrictions. In your case, you can just put the part of the chain that you want to "break" away inside your if
-statement:
听起来您想要分支,而不是中断- 您想要像往常一样继续到done
. Promise 的一个很好的特性是它们不仅可以链接,而且可以不受限制地嵌套和取消嵌套。在你的情况下,你可以把你想要“打破”的那部分链放在你的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
});
回答by jstaab
For folks using built-in browser promises and looking for a way to halt the promise chain without making all consumers know about the rejection case, triggering any chained then
's or catch
es or throwing any Uncaught (in promise)
errors, you can use the following:
对于使用内置浏览器承诺并寻找一种方法来停止承诺链而不让所有消费者知道拒绝情况、触发任何链接的then
's 或catch
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
Basically, noopPromise is a basic stubbed out promise interface that takes chaining functions, but never executes any. This relies on the fact that apparently the browser uses duck-typing to determine if something is a promise, so YMMV (I tested this in Chrome 57.0.2987.98), but if that becomes a problem you could probably create an actual promise instance and neuter its then and catch methods.
基本上,noopPromise 是一个基本的 stubped out promise 接口,它接受链接函数,但从不执行任何函数。这依赖于这样一个事实,即浏览器显然使用鸭子类型来确定某事是否是承诺,所以 YMMV(我在 Chrome 57.0.2987.98 中对此进行了测试),但如果这成为一个问题,您可能会创建一个实际的承诺实例和中性它的 then 和 catch 方法。