jquery - example - break promise chain
¿Cómo salir correctamente de una cadena de promesa? (3)
En primer lugar, creo que es mejor decir que está buscando "eludir" (parte de) la cadena de promesa en lugar de "romperla".
Como dices, probar "emptyResult" en varios lugares es bastante feo. Afortunadamente, un mecanismo más elegante está disponible mientras se adhiere al mismo principio general de no ejecutar parte de la cadena de promesa.
Un mecanismo alternativo es usar el rechazo prometedor para controlar el flujo, luego volver a detectar las condiciones de error específicas más adelante en la cadena y volver a colocarlo en la ruta de éxito.
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''
};
En el lado positivo, encuentro que la falta de anidamiento mejora la legibilidad de la ruta de éxito natural. Además, al menos para mí, este patrón requeriría un malabarismo mental mínimo para acomodar más derivaciones, si es necesario.
En el lado negativo, este patrón es ligeramente menos eficiente que el de Bergi.
Mientras que la ruta principal tiene el mismo número de promesas que la de Bergi, la ruta
cantinas.length == 0
requiere una más (o una por derivación si se codificaron varias derivaciones).
Además, este patrón requiere una nueva detección confiable de las condiciones específicas de error, de ahí el objeto
errMessages
, que algunos pueden encontrar restando valor.
En base a la pregunta aquí: jQuery encadenando y conectando en cascada los momentos y los cuándo y la respuesta aceptada, quiero romper la cadena de la promesa en un momento, pero aún no he encontrado la forma correcta. Hay multiple posts about esto, pero todavía estoy perdido.
Tomando el código de ejemplo de la pregunta original:
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
});
¿Cómo rompería la cadena si
cantinas.length == 0
?
No me gustaría obtener las comidas, ni los aditivos, francamente me gustaría llamar a algún tipo de devolución de llamada de "resultado vacío".
He intentado lo siguiente, que es
muy feo
(pero funciona ...).
Enséñame el camino correcto.
Este sigue siendo un resultado válido, por lo que no es un "error" per se, solo un resultado vacío, diría.
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
});
Para las personas que usan las promesas integradas del navegador y buscan una forma de detener la cadena de la promesa sin informar a todos los consumidores sobre el caso de rechazo, desencadenar cualquier encadenamiento o
catch
o arrojar cualquier error no
catch
Uncaught (in promise)
, puede usar el seguimiento:
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
Básicamente, noopPromise es una interfaz básica de promesa que toma funciones de encadenamiento, pero nunca ejecuta ninguna. Esto se basa en el hecho de que, aparentemente, el navegador utiliza la escritura de pato para determinar si algo es una promesa, por lo que YMMV (probé esto en Chrome 57.0.2987.98), pero si eso se convierte en un problema, probablemente podría crear una instancia de promesa real y neutral. es entonces y métodos de captura.
Parece que
quiere
ramificarse
,
no romperse
; desea continuar como de costumbre hasta el
done
.
Una buena propiedad de las promesas es que no solo se encadenan, sino que también se pueden
anidar y anular
sin restricciones.
En su caso, puede colocar la parte de la cadena que desea "romper" dentro de su declaración
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
});