Actualizar:
Para ayudar a los futuros espectadores de esta publicación, creé esta demostración de la respuesta de pluma .
Pregunta:
Mi objetivo parece bastante sencillo.
step(1)
.then(function() {
return step(2);
}, function() {
stepError(1);
return $q.reject();
})
.then(function() {
}, function() {
stepError(2);
});
function step(n) {
var deferred = $q.defer();
//fail on step 1
(n === 1) ? deferred.reject() : deferred.resolve();
return deferred.promise;
}
function stepError(n) {
console.log(n);
}
El problema aquí es que si fallo en el paso 1, ambos stepError(1)
Y stepError(2)
son despedidos. Si no lo hago return $q.reject
entonces stepError(2)
no va a ser despedido, pero step(2)
lo hará, que entiendo. He logrado todo excepto lo que estoy tratando de hacer.
¿Cómo escribo promesas para poder llamar a una función en caso de rechazo, sin llamar a todas las funciones en la cadena de error? ¿O hay otra forma de lograr esto?
Aquí hay una demostración en vivo para que tenga algo con lo que trabajar.
Actualizar:
De alguna manera lo he resuelto. Aquí, estoy captando el error al final de la cadena y pasando los datos para reject(data)
que sepa qué problema manejar en la función de error. En realidad, esto no cumple con mis requisitos porque no quiero depender de los datos. Sería lamentable, pero en mi caso sería más limpio pasar una devolución de llamada de error a la función en lugar de depender de los datos devueltos para determinar qué hacer.
Demostración en vivo aquí (clic).
step(1)
.then(function() {
return step(2);
})
.then(function() {
return step(3);
})
.then(false,
function(x) {
stepError(x);
}
);
function step(n) {
console.log('Step '+n);
var deferred = $q.defer();
(n === 1) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
console.log('Error '+n);
}
Promise.prototype.catch()
ejemplos en MDN muestran solución para exactamente los mismos problemas.Respuestas:
La razón por la que su código no funciona como se espera es que en realidad está haciendo algo diferente de lo que cree que hace.
Digamos que tienes algo como lo siguiente:
Para comprender mejor lo que está sucediendo, imaginemos que este es un código síncrono con
try
/catch
blocks:El
onRejected
controlador (el segundo argumento dethen
) es esencialmente un mecanismo de corrección de errores (como uncatch
bloque). Si se genera un errorhandleErrorOne
, será detectado por el siguiente bloque de captura (catch(e2)
), y así sucesivamente.Obviamente, esto no es lo que pretendías.
Digamos que queremos que toda la cadena de resolución falle sin importar lo que salga mal:
Nota: Podemos dejar el lugar
handleErrorOne
donde está, porque solo se invocará si sestepOne
rechaza (es la primera función en la cadena, por lo que sabemos que si la cadena se rechaza en este punto, solo puede ser debido a la promesa de esa función) .El cambio importante es que los manejadores de errores para las otras funciones no son parte de la cadena principal de promesa. En cambio, cada paso tiene su propia "subcadena" con una
onRejected
que solo se llama si el paso fue rechazado (pero la cadena principal no puede alcanzarlo directamente).La razón por la que esto funciona es que tanto
onFulfilled
yonRejected
son argumentos opcionales althen
método. Si se cumple una promesa (es decir, se resuelve) y el siguientethen
en la cadena no tiene unonFulfilled
controlador, la cadena continuará hasta que haya uno con dicho controlador.Esto significa que las siguientes dos líneas son equivalentes:
Pero la siguiente línea no es equivalente a las dos anteriores:
La biblioteca de promesa de Angular
$q
se basa en laQ
biblioteca de kriskowal (que tiene una API más rica, pero contiene todo lo que puede encontrar$q
). Los documentos API de Q en GitHub podrían resultar útiles. Q implementa la especificación Promises / A + , que detalla cómothen
y cómo funciona exactamente el comportamiento de resolución de promesas.EDITAR:
También tenga en cuenta que si desea salir de la cadena en su controlador de errores, debe devolver una promesa rechazada o lanzar un Error (que será atrapado y envuelto en una promesa rechazada automáticamente). Si no devuelve una promesa,
then
envuelve el valor de devolución en una promesa de resolución para usted.Esto significa que si no devuelve nada, efectivamente está devolviendo una promesa resuelta por el valor
undefined
.fuente
if you don't return anything, you are effectively returning a resolved promise for the value undefined.
Gracias @plumastepOne().then(stepTwo, handleErrorOne)
`stepOne (). then (null, handleErrorOne) .then (stepTwo)` ¿Son realmente equivalentes? Creo que en caso de rechazo enstepOne
la segunda línea de código se ejecutará,stepTwo
pero la primera solo se ejecutaráhandleErrorOne
y se detendrá. ¿O me estoy perdiendo algo?Un poco tarde para la fiesta, pero esta solución simple funcionó para mí:
Esto le permite romper fuera de la cadena.
fuente
.then(user => { if (user) return Promise.reject('The email address already exists.') })
.then(user => { if (user) throw 'The email address already exists.' })
Lo que necesita es una
.then()
cadena repetitiva con un estuche especial para comenzar y un estuche especial para terminar.La habilidad es obtener el número de paso del caso de falla para pasar a un controlador de error final.
step(1)
incondicionalmente..then()
con las siguientes devoluciones de llamada:.then()
controlador sin éxito y un controlador de error final.Puede escribir todo a mano pero es más fácil demostrar el patrón con funciones generalizadas con nombre:
ver demo
Observe cómo
step()
, el diferido se rechaza o resuelve conn
, haciendo que ese valor esté disponible para las devoluciones de llamada en el siguiente.then()
en la cadena. Una vez questepError
se llama, el error se vuelve a lanzar repetidamente hasta que lo manejafinalError
.fuente
Al rechazar, debe pasar un error de rechazo, luego envolver los manejadores de errores de paso en una función que verifique si el rechazo debe procesarse o "volverse a lanzar" hasta el final de la cadena:
Lo que verías en la consola:
Aquí hay un código de trabajo https://jsfiddle.net/8hzg5s7m/3/
Si tiene un manejo específico para cada paso, su contenedor podría ser algo como:
entonces tu cadena
fuente
Si entiendo correctamente, solo desea que se muestre el error para el paso que falla, ¿verdad?
Eso debería ser tan simple como cambiar el caso de fracaso de la primera promesa a esto:
Al regresar
$q.reject()
en el caso de falla del primer paso, está rechazando esa promesa, lo que hace que se llame al errorCallback en el segundothen(...)
.fuente
step(2)
. Ahora lo intenté de nuevo, no está sucediendo. Estoy tan confundida.return step(2);
solo debería llamarse cuando sestep(1)
resuelve con éxito.return $q.reject()
, la cadena continuará. En este caso loreturn response
estropeó. Vea esto: jsbin.com/EpaZIsIp/6/edithttp://jsbin.com/EpaZIsIp/20/edit
O automatizado para cualquier número de pasos:
http://jsbin.com/EpaZIsIp/21/edit
fuente
deferred.reject(n)
entonces yo estoy haciendo la advertencia que prometen rechazó con un objeto nonErrorIntente usar esto como libs:
https://www.npmjs.com/package/promise-chain-break
fuente
Si desea resolver este problema usando async / await:
fuente
Adjunte controladores de errores como elementos de cadena separados directamente a la ejecución de los pasos:
o usando
catch()
:Nota: Este es básicamente el mismo patrón que sugiere pluma en su respuesta, pero usando el nombre del OP.
fuente
Los
Promise.prototype.catch()
ejemplos encontrados en MDN a continuación son muy útiles.(La respuesta aceptada menciona
then(null, onErrorHandler)
que es básicamente lo mismo quecatch(onErrorHandler)
).fuente
La mejor solución es refactorizar su cadena de promesa para usar ES6 en espera. Luego puede regresar de la función para omitir el resto del comportamiento.
He estado golpeándome la cabeza contra este patrón durante más de un año y usar el esperar es el cielo.
fuente
Utilice un módulo de promesa secuencial
Intención
Proporcione un módulo cuya responsabilidad sea ejecutar las solicitudes de forma secuencial, mientras realiza el seguimiento del índice actual de cada operación de manera ordinal. Defina la operación en un Patrón de comando para mayor flexibilidad.
Participantes
execute
método para encadenar y rastrear cada operación. SequentialPromise devuelve una Promise-Chain de todas las operaciones realizadas.execute
método mientras pasa una lista ordinal de opciones para cada operación.Consecuencias
Use SequentialPromise cuando se necesita un comportamiento ordinal de resolución Promise. SequentialPromise rastreará el índice para el que se rechazó una Promesa.
Implementación
Esencia
Promesa secuencial
fuente
Si en algún momento regresas
Promise.reject('something')
, serás arrojado a la trampa de la promesa.Si la primera promesa no devuelve ningún resultado, solo obtendrá 'Sin resultado' en la consola.
fuente