Me topé con una forma interesante pero bastante pirateada de escribir código lineal con promesas en Javascript, de la siguiente manera:
const return_by_death = new Promise((resolve) => {});y
const signup = async (req, res) => { const user = req.body.username; const pass = req.body.password; if(!user || !pass) { res.sendStatus(400); return; } const hash_data = await generate_hash(pass).catch(async (err) => { res.sendStatus(500); await return_by_death; }); const new_account = new models.account.model({ username: user, salt: hash_data.salt, hash: hash_data.hash }); // ... };y desde mi experimentación, parece funcionar de manera que si se rechaza la promesa de generate_hash, irá a mi controlador de errores sin pasar a la línea new_account. Mis preguntas son las siguientes:
¿Esto desperdicia memoria al generar promesas o hilos de ejecución que simplemente se cuelgan indefinidamente?
¿Expressjs se aferra a las funciones pasadas a app.get(path, func) donde estaría realizando un seguimiento de la promesa de registro que quizás nunca se resuelva?
¿Hay una mejor manera de hacer esto?
EDITAR: según la respuesta/información de @CertainPerformance, se me ocurrió la siguiente solución
class PromiseRunError { constructor(obj) { this.obj = obj; } } Promise.prototype.run = function() { return this.catch((err) => {return new PromiseRunError(err)}).then((data) => { if(data instanceof PromiseRunError) { return [undefined, data.obj]; } return [data, undefined]; }); };y
const [hash_data, hash_gen_err] = await generate_hash(pass).run(); if(hash_gen_err) { res.sendStatus(500); return; }Sí, usar Promesas perpetuamente no resueltas aquí es un problema.
signup muchas veces y se generan muchas Promesas que se bloquean, se usará más y más memoria con el tiempo; cada llamada de signup da como resultado un nuevo cierre con las variables user , pass , req y res asignadas, que no se pueden recolectar basura hasta que finaliza la función. (Pero nunca terminará si hay un error). Si desea mantener las cosas lo más planas posible, sin rodear todo en un try / catch , asumiendo que generate_hash devuelve una Promesa que, si se resuelve, se resolverá en algo veraz, simplemente no devuelva nada dentro del controlador .catch , luego verifique si hash_data es verdadero o no antes de continuar:
const signup = async (req, res) => { const user = req.body.username; const pass = req.body.password; if(!user || !pass) { res.sendStatus(400); return; } const hash_data = await generate_hash(pass).catch(() => {}); if (!hash_data) { res.sendStatus(500); return; } const new_account = new models.account.model({ username: user, salt: hash_data.salt, hash: hash_data.hash }); // ... }; Sin embargo, preferiría try / catch :
const signup = async (req, res) => { try { const { user, pass } = req.body; if (!user || !pass) { res.sendStatus(400); return; } const hash_data = await generate_hash(pass) const new_account = new models.account.model({ username: user, salt: hash_data.salt, hash: hash_data.hash }); // ... } catch (e) { res.sendStatus(500); return; } };