如何正确地打破承诺链?
How to properly break out of a promise chain?
基于这里的问题:jQuery chaining and cascading then's and when's and the accepted answer, I want to break the promise chain at a point but haven't yet found the correct way. There are multiple posts 这个,但我还是迷路了。
从原始问题中获取示例代码:
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
我将如何打破链条?我不想吃饭,也不想吃添加剂,坦率地说,我想调用某种 "empty result" 回调。我已经尝试了以下非常难看(但有效...)。教我正确的方法。这仍然是一个有效的结果,所以不是 "fail" 本身,我会说只是空结果。
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
});
听起来像你 , 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 unnested 没有限制。在你的情况下,你可以把你想要 "break" 的链的一部分放在你的 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
});
首先,我认为最好说您正在寻求 "bypass"(部分)承诺链而不是 "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
对象 - 有些人可能会觉得有损。
对于使用内置浏览器 promises 并寻找一种方法来停止 promise 链而不让所有消费者都知道拒绝情况的人,触发任何链式 then
或 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
基本上,noopPromise 是一个基本的 stubbed promise 接口,它接受链接函数,但从不执行任何函数。这依赖于这样一个事实,即浏览器显然使用 duck-typing 来确定某事是否是一个承诺,所以 YMMV(我在 Chrome 57.0.2987.98 中对此进行了测试),但如果这成为一个问题,你可能会创建一个实际的承诺实例并中性化其 then 和 catch 方法。
基于这里的问题:jQuery chaining and cascading then's and when's and the accepted answer, I want to break the promise chain at a point but haven't yet found the correct way. There are multiple posts
从原始问题中获取示例代码:
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
我将如何打破链条?我不想吃饭,也不想吃添加剂,坦率地说,我想调用某种 "empty result" 回调。我已经尝试了以下非常难看(但有效...)。教我正确的方法。这仍然是一个有效的结果,所以不是 "fail" 本身,我会说只是空结果。
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
});
听起来像你 done
. A nice property of promises is that they don't only chain, but also can be nested and unnested 没有限制。在你的情况下,你可以把你想要 "break" 的链的一部分放在你的 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
});
首先,我认为最好说您正在寻求 "bypass"(部分)承诺链而不是 "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
对象 - 有些人可能会觉得有损。
对于使用内置浏览器 promises 并寻找一种方法来停止 promise 链而不让所有消费者都知道拒绝情况的人,触发任何链式 then
或 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
基本上,noopPromise 是一个基本的 stubbed promise 接口,它接受链接函数,但从不执行任何函数。这依赖于这样一个事实,即浏览器显然使用 duck-typing 来确定某事是否是一个承诺,所以 YMMV(我在 Chrome 57.0.2987.98 中对此进行了测试),但如果这成为一个问题,你可能会创建一个实际的承诺实例并中性化其 then 和 catch 方法。