El Tao de las Promesas
Con un poco más de experiencia y comprensión de las Promesas y las leyes monádicas que rigen su uso, no habríamos pasado tanto tiempo trabajando en el refactorizador de esta pieza de código.
En JavaScript, el código asíncrono se puede manejar a través de Promesas, que son las mónadas de continuación de JavaScript. Lo más importante en este caso de que las Promesas sean una mónada es que la siguiente ley es verdadera:
Promise.resolve(Promise.resolve(x)) === Promise.resolve(x).
Y más importante que la anterior, esta también se aplica a cualquier valor x:
Promise.resolve(Promise.reject(x)) === Promise.reject(Promise.resolve(x)) === Promise.reject(x).
Esta regla mágica de arriba es lo que me salvó un poco de poder de pensamiento al final del día. Esto significaba que podía tratar esos errores tan pronto como sucedieran, dentro de sus propias funciones, manteniéndome alejado de esa locura de cadena larga. La respuesta siempre estaba ahí mirándome, simplemente no podía verla. Ahora veo, y es hermoso.
Esto significaba que yo podía tener solo el saveApplication función como esta, por ejemplo:
function saveApplication() {
return makeApiCall().catch((err) => Promise.reject('basic'));
}
El .bloque catch significa que estamos manejando un error en el paso básico del formulario, porque la llamada saveApplication está relacionada con el paso del formulario llamado basic. Esto nos llevó a la hermosa pieza de código de abajo:
saveApplication()
.then(uploadImages)
.then(saveService)
.then(savePricingInfo)
.then(savePaymentInfo)
.then(gotoMainPage)
.catch((step) => {
setErrorState();
multiStepManager.go(step);
});
Solo tuvimos que cambiar una sola línea de la cadena de promesas, ahora que todas las funciones dentro de la .a continuación, blocks devuelve una Promesa que ya rechaza al paso correspondiente.
Pero, ¿qué pasaría si ocurrieran otros tipos de errores, que no fueran manejados por las capturas internas?
Bueno, eso se podría resolver fácilmente implementando tipos de error personalizados y separando la evaluación de diferentes tipos de error dentro de nuestro principal.bloqueo de captura. Como esto:
function saveApplication() {
return makeApiCall().catch((err) => Promise.reject(new StepError('basic')));
}//
saveApplication()
.then(uploadImages)
.then(saveService)
.then(savePricingInfo)
.then(savePaymentInfo)
.then(gotoMainPage)
.catch((step) => {
if(err instanceof StepError) {
setErrorState();
multiStepManager.go(step);
}
else {
// handle other types of errors
}
});
En este caso, el principal .catch block solo maneja errores de tipo StepError. Otros tipos de errores son simplemente lanzados, no rechazados, para que la aplicación o el navegador puedan manejarlos en consecuencia.
El mismo principio puede y debe ampliarse para manejar tipos de error específicos, como diferentes estados HTTP.