¿Debo regresar después de una resolución/rechazo anticipado?

Resuelto sam asked hace 9 años • 6 respuestas

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 rejectsalir temprano, ¿debería adquirir el hábito de returnhacerlo inmediatamente después también?

sam avatar Sep 12 '15 13:09 sam
Aceptado

El returnpropó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:

  1. pendiente - estado inicial. De pendiente podemos pasar a uno de los otros estados.
  2. cumplido - operación exitosa
  3. 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));
Expandir fragmento

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));
Expandir fragmento

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 returnopciones porque el código es más plano.

Ori Drori avatar Sep 12 '2015 06:09 Ori Drori

Un modismo común, que puede ser de su agrado o no, es combinar the returncon 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, resolveno 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, rejectno 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 divideno espera que devuelva nada y no hace nada con el valor devuelto.

 avatar Mar 19 '2016 07:03