Al desarrollar un microservicio simple en hapijs, me di cuenta que al siempre utilizar async/await casi era obligado en ciertos escenarios utilizar try/catch (Definido así desde arquitectura inicial del proyecto), lo cual no me tenía del todo convencido y recien empezando con Go me di cuenta que no manejan este tipo de excepciones por los siguientes principios.
- Usar excepciones para errores esperados es una complejidad básicamente innecesaria.
- Además, mucha gente ha notado que mover el manejo de errores lejos del origen del error hace que las personas no piensen en las rutas de error.
- Cuando tienes que pensar qué hacer con el error en cada llamada, terminas pensando en las rutas de error, lo que conduce a un código de mayor calidad.
Pongamos un ejemplo
export async function get(req, res) {
try {
const users = await Users.find();
if (users && users.length > 0) {
// hacer algo de negocio
}
return users;
} catch (err) {
throw boom.boomify(err);
}
}
Este es el código inicial y me pareció buena idea realizar un manejador para los casos donde algo falle e intentar programar algo más modular.
Un handler
async function handle(promise) {
const response = {
data: undefined,
error: undefined,
};
if (!promise || typeof promise !== "function") {
response.error = "Not promise";
return response;
}
await promise()
.then((data) => {
response.data = data;
})
.catch((error) => {
response.error = error;
});
return response;
}
Es sencillo, definimos un objeto con dos atributos: data y error.
const response = {
data: undefined,
error: undefined,
};
Para casos donde no mandemos el parámetro de promesa en la function, se podría validar así:
if (!promise || typeof promise !== "function") {
response.error = "Not promise";
return response;
}
Por último ejecutamos el promise y retornamos:
await promise()
.then((data) => {
response.data = data;
})
.catch((error) => {
response.error = error;
});
return response;
El ejemplo práctico podría funcionar de la siguiente manera:
export async function get(req, res) {
const users = await handle(() => Users.find());
if (users.error) {
// aquí ocurrió un error y se puede tomar decisión
throw boom.boomify(users.error);
}
if (users.data && users.data.length > 0) {
// hacer algo de negocio
}
return users.data;
}
Como lo vemos ya no dependemos de try/catch, es un código que en definitiva puede aportar a la legibilidad y reducir los posibles bugs al tener un control más preciso sobre los errores.
¿Qué mejorarías, o te quedarías con try/catch para siempre?