Todavía soy bastante nuevo en las promesas y estoy usando bluebird actualmente, sin embargo, tengo un escenario en el que no estoy muy seguro de cómo tratarlo mejor.
Entonces, por ejemplo, tengo una cadena de promesa dentro de una aplicación express como esta:
repository.Query(getAccountByIdQuery)
.catch(function(error){
res.status(404).send({ error: "No account found with this Id" });
})
.then(convertDocumentToModel)
.then(verifyOldPassword)
.catch(function(error) {
res.status(406).send({ OldPassword: error });
})
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
Entonces el comportamiento que busco es:
- Va a obtener cuenta por ID
- Si hay un rechazo en este punto, bombardea y devuelve un error
- Si no hay error, convierta el documento devuelto a un modelo
- Verifique la contraseña con el documento de la base de datos
- Si las contraseñas no coinciden, bombardea y devuelve un error diferente
- Si no hay error, cambie las contraseñas
- Luego devuelve el éxito
- Si algo más salió mal, devuelve 500
Entonces, las capturas actuales no parecen detener el encadenamiento, y eso tiene sentido, por lo que me pregunto si hay una manera de forzar de alguna manera a la cadena a detenerse en un cierto punto en función de los errores, o si hay una mejor manera estructurar esto para obtener algún tipo de comportamiento de ramificación, como es el caso de if X do Y else Z
.
Cualquier ayuda sería genial.
javascript
node.js
promise
bluebird
Grofit
fuente
fuente
Respuestas:
Este comportamiento es exactamente como un lanzamiento sincrónico:
Esa es la mitad del punto de
.catch
poder recuperarse de los errores. Puede ser conveniente volver a lanzar para indicar que el estado sigue siendo un error:Sin embargo, esto solo no funcionará en su caso ya que el error será detectado por un controlador posterior. El problema real aquí es que los manejadores de errores generalizados "MANEJAR CUALQUIER COSA" son una mala práctica en general y están muy mal vistos en otros lenguajes de programación y ecosistemas. Por esta razón, Bluebird ofrece capturas tipificadas y predicadas.
La ventaja adicional es que su lógica de negocios no tiene (y no debería) tener que conocer el ciclo de solicitud / respuesta. No es responsabilidad de la consulta decidir qué estado HTTP y error obtiene el cliente y, a medida que su aplicación crezca, es posible que desee separar la lógica de negocios (cómo consultar su base de datos y cómo procesar sus datos) de lo que envía al cliente (qué código de estado http, qué texto y qué respuesta).
Así es como escribiría tu código.
Primero,
.Query
lanzaría unNoSuchAccountError
, lo subclase de loPromise.OperationalError
que Bluebird ya proporciona. Si no está seguro de cómo subclasificar un error, avíseme.Además, lo subclasificaría
AuthenticationError
y luego haría algo como:Como puede ver, está muy limpio y puede leer el texto como un manual de instrucciones de lo que sucede en el proceso. También está separado de la solicitud / respuesta.
Ahora, lo llamaría desde el controlador de ruta como tal:
De esta manera, la lógica está en un solo lugar y la decisión de cómo manejar los errores al cliente está en un solo lugar y no se abarrotan entre sí.
fuente
.catch(someSpecificError)
controlador intermedio para algún error específico es si desea detectar un tipo específico de error (que es inofensivo), tratarlo y continuar el flujo que sigue. Por ejemplo, tengo un código de inicio que tiene una secuencia de cosas que hacer. Lo primero es leer el archivo de configuración del disco, pero si ese archivo de configuración falla, es un error correcto (el programa ha incorporado los valores predeterminados) para que pueda manejar ese error específico y continuar el resto del flujo. También puede haber una limpieza mejor para no irse hasta más tarde.instanceof
chceks manualmente tú mismo..catch
funciona como latry-catch
declaración, lo que significa que solo necesita una captura al final:fuente
No. No puedes realmente "terminar" una cadena, a menos que arrojes una excepción que burbujee hasta su final. Vea la respuesta de Benjamin Gruenbaum sobre cómo hacer eso.
Una derivación de su patrón sería no distinguir los tipos de error, sino utilizar los errores que tienen
statusCode
y losbody
campos que pueden enviarse desde un único.catch
controlador genérico . Dependiendo de la estructura de su aplicación, su solución podría ser más limpia.Sí, puedes hacer ramificaciones con promesas . Sin embargo, esto significa abandonar la cadena y "volver" a anidar, tal como lo haría en una instrucción anidada if-else o try-catch:
fuente
He estado haciendo de esta manera:
Dejas tu captura al final. Y solo arroje un error cuando ocurra a mitad de su cadena.
Sus otras funciones probablemente se verían así:
fuente
Probablemente un poco tarde para la fiesta, pero es posible anidar
.catch
como se muestra aquí:Red de desarrolladores de Mozilla: uso de promesas
Editar: envié esto porque proporciona la funcionalidad solicitada en general. Sin embargo, no lo hace en este caso particular. Porque como ya explicaron en detalle otros,
.catch
se supone que debe recuperar el error. No se puede, por ejemplo, enviar una respuesta al cliente en múltiples.catch
devoluciones de llamada porque un.catch
sin explícitasreturn
resuelve TI conundefined
en ese caso, causando procedimiento.then
de desencadenar pesar de que su cadena no está realmente resuelto, que puede causar una siguiente.catch
al gatillo y el envío otra respuesta al cliente, causando un error y probablemente arrojando unUnhandledPromiseRejection
rumbo. Espero que esta oración enrevesada tenga algún sentido para ti.fuente
En lugar de
.then().catch()...
que puedas hacer.then(resolveFunc, rejectFunc)
. Esta cadena de promesa sería mejor si manejas las cosas en el camino. Así es como lo reescribiría:Nota: El
if (error != null)
es un poco de un truco para interactuar con el error más reciente.fuente
Creo que la respuesta de Benjamin Gruenbaum anterior es la mejor solución para una secuencia lógica compleja, pero esta es mi alternativa para situaciones más simples. Solo uso una
errorEncountered
bandera junto conreturn Promise.reject()
para omitir cualquier posteriorthen
ocatch
declaraciones. Entonces se vería así:Si tiene más de dos pares / catch, probablemente debería usar la solución de Benjamin Gruenbaum. Pero esto funciona para una configuración simple.
Tenga en cuenta que la final
catch
solo tiene enreturn;
lugar dereturn Promise.reject();
, porque no hay subsecuentesthen
que debamos omitir, y contaría como un rechazo de Promesa no controlado, que a Node no le gusta. Como está escrito arriba, la finalcatch
devolverá una promesa resuelta pacíficamente.fuente