¿Debo regresar después de una resolución/rechazo anticipado?
Supongamos que tengo el siguiente código.
function divide(numerator, denominator) {
return new Promise((resolve, reject) => {
if(denominator === 0){
reject("Cannot divide by 0");
return; //superfluous?
}
resolve(numerator / denominator);
});
}
Si mi objetivo es reject
salir temprano, ¿debería adquirir el hábito de return
hacerlo inmediatamente después también?
El return
propósito es finalizar la ejecución de la función después del rechazo e impedir la ejecución del código después del mismo.
function divide(numerator, denominator) {
return new Promise((resolve, reject) => {
if (denominator === 0) {
reject("Cannot divide by 0");
return; // The function execution ends here
}
resolve(numerator / denominator);
});
}
En este caso impide que se resolve(numerator / denominator);
ejecute, lo cual no es estrictamente necesario. Sin embargo, sigue siendo preferible finalizar la ejecución para evitar una posible trampa en el futuro. Además, es una buena práctica evitar la ejecución innecesaria de código.
Fondo
Una promesa puede estar en uno de 3 estados:
- pendiente - estado inicial. De pendiente podemos pasar a uno de los otros estados.
- cumplido - operación exitosa
- rechazado - operación fallida
Cuando una promesa se cumple o se rechaza, permanecerá en este estado indefinidamente (resuelta). Así, rechazar una promesa cumplida o cumplir una promesa rechazada, no tendrá efecto.
Este fragmento de ejemplo muestra que, aunque la promesa se cumplió después de ser rechazada, permaneció rechazada.
function divide(numerator, denominator) {
return new Promise((resolve, reject) => {
if (denominator === 0) {
reject("Cannot divide by 0");
}
resolve(numerator / denominator);
});
}
divide(5,0)
.then((result) => console.log('result: ', result))
.catch((error) => console.log('error: ', error));
Entonces, ¿por qué necesitamos regresar?
Aunque no podemos cambiar el estado de una promesa establecida, rechazarla o resolverla no detendrá la ejecución del resto de la función. La función puede contener código que generará resultados confusos. Por ejemplo:
function divide(numerator, denominator) {
return new Promise((resolve, reject) => {
if (denominator === 0) {
reject("Cannot divide by 0");
}
console.log('operation succeeded');
resolve(numerator / denominator);
});
}
divide(5, 0)
.then((result) => console.log('result: ', result))
.catch((error) => console.log('error: ', error));
Incluso si la función no contiene dicho código en este momento, esto crea una posible trampa en el futuro. Una futura refactorización podría ignorar el hecho de que el código aún se ejecuta después de que se rechaza la promesa y será difícil de depurar.
Detener la ejecución después de resolver/rechazar:
Esto es material de flujo de control JS estándar.
- Regresar después de
resolve
/reject
:
Mostrar fragmento de código
- Regrese con
resolve
/reject
- dado que el valor de retorno de la devolución de llamada se ignora, podemos guardar una línea devolviendo la declaración de rechazo/resolver:
Mostrar fragmento de código
- Usando un bloque if/else:
Mostrar fragmento de código
Prefiero usar una de las return
opciones porque el código es más plano.
Un modismo común, que puede ser de su agrado o no, es combinar the return
con the reject
, para rechazar simultáneamente la promesa y salir de la función, de modo que el resto de la función, incluido the, resolve
no se ejecute. Si te gusta este estilo, puede hacer que tu código sea un poco más compacto.
function divide(numerator, denominator) {
return new Promise((resolve, reject) => {
if (denominator === 0) return reject("Cannot divide by 0");
^^^^^^^^^^^^^^
resolve(numerator / denominator);
});
}
Esto funciona bien porque el constructor de Promise no hace nada con ningún valor de retorno y, en cualquier caso resolve
, reject
no devuelve nada.
Se puede usar el mismo modismo con el estilo de devolución de llamada que se muestra en otra respuesta:
function divide(nom, denom, cb){
if(denom === 0) return cb(Error("Cannot divide by zero"));
^^^^^^^^^
cb(null, nom / denom);
}
Nuevamente, esto funciona bien porque la persona que llama divide
no espera que devuelva nada y no hace nada con el valor devuelto.