Tengo un controlador de registro que usa el mecanismo try {} catch(e) {} para mí, es un mecanismo ad-hoc bastante decente para el error, pero en general es mejor cambiar a promesas, ya que creo que las promesas ofrecen mecanismos de manejo de errores más detallados, así que a continuación es mi código a partir de ahora en mi controlador.
const registerNewUser = async (req, res) => { /* code here for extracting info from req */ try { const users_data = await users.create_new_user({ email, crypted_pwd, salt, first_name, phone_code, }); const { id: user_id, first_name: user_first_name } = users_data; const customer_data = await customers.create_new_customer({ first_name, last_name, email, }); const { tenant_id, id: customer_id, } = customer_data; await clearCartFromRedis(merchant_canonical_name, req.token); signUpWelcomeMailer(merchant_canonical_name, req.token, user_id); return res.status(200).json({ success: true, access_token: req.token, first_name: user_first_name, }); } catch (error) { logger.log({ message: error.message, level: 'error' }); return res.status(500).send(error.message); } }; Como puede ver, suceden varias cosas dentro del bloque de prueba. Consulta de IE para secuencializar a create_new_user y create_new_customer , luego, clearCartFromRedis , y luego signUpWelcomeMailer . Los tres pueden arrojar diferentes tipos de errores y, sin embargo, los he manejado en un solo bloque catch dentro del cual tengo la siguiente declaración.
return res.status(500).send(error.message); Así que ahora mi pregunta es: ¿hubiera sido mejor manejar esto en una cadena de promesas usando then() y catch() ? así que diga refactorizar lo anterior a algo como
customers.create_new_customer({ first_name, last_name, email, }).then(data => { const obj = { tenant_id: data.tenant_id, id: data.customer_id, merchant_canonical_name: data.merchant_canonical_name } return obj }).then((obj) => { clearCartFromRedis() .catch(() => { throw new Error('Could not clear cache') }) }).then(() => { signUpWelcomeMailer(merchant_canonical_name, req.token, user_id) .catch(() => { throw new Error('Error encountered while sending the email') }) }).catch(error => res.sendStatus(500)) Entonces, como puede ver, hay un manejo de errores individuales, así como un bloque de captura global al final. Mi pregunta es, en mi caso de uso, es un try ... catch mejor o el mecanismo de encadenamiento de promesas?
PD: hay una pregunta similar hecha AQUÍ , pero esta pregunta se refiere más al mecanismo de encadenamiento de try .. catch vs promise chaining mechanism .
En primer lugar, try... catch es útil para detectar la mayoría de los errores expected (cuando necesita devolver algún error 4xxx con información adicional o incluso 200 con un código de error de lógica empresarial o algo así) en lugar de algunos errores inesperados que deberían conducir a 500 Errores HTTP. Dicho esto, necesita un controlador global para todos los errores no detectados/inesperados que registre estos errores y envíe el error 500 a un cliente.
controlador de errores globales
app.use(function (err, req, res, next) { logger.error(err) res.status(500).send('Unexpected error') })probar/atrapar con una transacción explícita
const registerNewUser = async (req, res) => { /* code here for extracting info from req */ // here is some call to create a transaction in a DB // it varies depending on a type of DB and a package you use to communicate with it // we need to create a transaction BEFORE try/catch block const transaction = await createTransaction(); try { // we need to pass transaction to every method that modifies data in DB // also depends on how to pass a transaction to underlying DB package methods const users_data = await users.create_new_user({ email, crypted_pwd, salt, first_name, phone_code, }, transaction); const { id: user_id, first_name: user_first_name } = users_data; // we need to pass transaction to every method that modifies data in DB const customer_data = await customers.create_new_customer({ first_name, last_name, email, }, transaction); const { tenant_id, id: customer_id, } = customer_data; await clearCartFromRedis(merchant_canonical_name, req.token); signUpWelcomeMailer(merchant_canonical_name, req.token, user_id); // here we need to commit the transaction before we exit from this handler await transaction.commit(); return res.status(200).json({ success: true, access_token: req.token, first_name: user_first_name, }); } catch (error) { // here we need to rollback the transaction before we exit from this handler await transaction.rollback(); logger.log({ message: error.message, level: 'error' }); return res.status(500).send(error.message); } }; En segundo lugar, en su caso de varias operaciones contra alguna base de datos, claramente necesita usar un mecanismo de transacción para mantener la coherencia de los datos. Por lo general, requiere crear una transacción explícita y pasarla a cada consulta que debe ejecutarse como una operación atómica que se ejecuta correctamente (entonces debe confirmar una transacción) o falla (entonces debe revertir una transacción). Para eso, puede usar try...catch para poder confirmar o revertir una transacción al final de un lote de operaciones.