style attribute javascript promise es6-promise

javascript - attribute - ¿Tengo que regresar después de una resolución/rechazo temprano?



title css (6)

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 usar el reject para salir temprano, ¿debería adquirir el hábito de return inmediatamente después?


El propósito de la return es terminar la ejecución de la función después del rechazo y evitar la ejecución del código después de ella.

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 evita la resolve(numerator / denominator); de ejecutar, lo cual no es estrictamente necesario. Sin embargo, aún es preferible terminar la ejecución para evitar una posible trampa en el futuro. Además, es una buena práctica evitar que se ejecute código innecesariamente.

Antecedentes

Una promesa puede ser en uno de los 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 se cumple o se rechaza una promesa, permanecerá en este estado indefinidamente (resuelta). Por lo tanto, 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, se mantuvo 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 volver?

Aunque no podemos cambiar un estado de promesa establecido, rechazar o resolver no detendrá la ejecución del resto de la función. La función puede contener código que creará 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 ese código en este momento, esto crea una posible trampa futura. Un futuro refactor 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 la resolve / reject :

function divide(numerator, denominator) { return new Promise((resolve, reject) => { if (denominator === 0) { reject("Cannot divide by 0"); return; } console.log(''operation succeeded''); resolve(numerator / denominator); }); } divide(5, 0) .then((result) => console.log(''result: '', result)) .catch((error) => console.log(''error: '', error));

  • Regrese con la resolve / reject : dado que se ignora el valor de retorno de la devolución de llamada, podemos guardar una línea devolviendo la declaración de rechazo / resolución:

function divide(numerator, denominator) { return new Promise((resolve, reject) => { if (denominator === 0) { return 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));

  • Usando un bloque if / else:

function divide(numerator, denominator) { return new Promise((resolve, reject) => { if (denominator === 0) { reject("Cannot divide by 0"); } else { console.log(''operation succeeded''); resolve(numerator / denominator); } }); } divide(5, 0) .then((result) => console.log(''result: '', result)) .catch((error) => console.log(''error: '', error));

Prefiero usar una de las opciones de return ya que el código es más plano.


En muchos casos, es posible validar los parámetros por separado e inmediatamente devolver una promesa rechazada con Promise.reject(reason) .

function divide2(numerator, denominator) { if (denominator === 0) { return Promise.reject("Cannot divide by 0"); } return new Promise((resolve, reject) => { resolve(numerator / denominator); }); } divide2(4, 0).then((result) => console.log(result), (error) => console.log(error));


La respuesta de Ori ya explica que no es necesario return pero es una buena práctica. Tenga en cuenta que el constructor de promesas es seguro, por lo que ignorará las excepciones lanzadas que se pasaron más adelante en el camino, esencialmente tiene efectos secundarios que no puede observar fácilmente.

Tenga en cuenta que return temprano también es muy común en las devoluciones de llamada:

function divide(nom, denom, cb){ if(denom === 0){ cb(Error("Cannot divide by zero"); return; // unlike with promises, missing the return here is a mistake } cb(null, nom / denom); // this will divide by zero. Since it''s a callback. }

Entonces, si bien es una buena práctica en las promesas, se requiere con devoluciones de llamada. Algunas notas sobre su código:

  • Su caso de uso es hipotético, en realidad no use promesas con acciones sincrónicas.
  • El constructor de la promesa ignora los valores de retorno. Algunas bibliotecas le avisarán si devuelve un valor no definido para advertirle contra el error de regresar allí. La mayoría no son tan inteligentes.
  • El constructor de la promesa es seguro, convertirá las excepciones en rechazos, pero como otros han señalado, una promesa se resuelve una vez.

Si no "regresa" después de una resolución / rechazo, pueden ocurrir cosas malas (como el redireccionamiento de una página) después de que pretendía que se detuviera. Fuente: Me encontré con esto.


Un modismo común, que puede o no ser su taza de té, es combinar el return con el reject , para rechazar simultáneamente la promesa y salir de la función, de modo que el resto de la función, incluida la 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 Promise no hace nada con ningún valor de retorno, y en cualquier caso, resolve y reject no devuelve nada.

El mismo idioma se puede usar 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 de retorno.


Técnicamente no es necesario aquí 1 , porque una Promesa puede resolverse o rechazarse, exclusivamente y solo una vez. El primer resultado de Promise gana y cada resultado posterior se ignora. Esto es diferente de las devoluciones de llamada de estilo de nodo.

Dicho esto, es una buena práctica limpia asegurarse de que se llama exactamente uno, cuando sea práctico, y de hecho en este caso ya que no hay más procesamiento asíncrono / diferido. La decisión de "regresar temprano" no es diferente a finalizar cualquier función cuando se completa su trabajo , en lugar de continuar el procesamiento no relacionado o innecesario.

Regresar en el momento apropiado (o de otra manera usar condicionales para evitar ejecutar el "otro" caso) reduce la posibilidad de ejecutar accidentalmente el código en un estado no válido o realizar efectos secundarios no deseados; y como tal hace que el código sea menos propenso a ''romperse inesperadamente''.

1 Esta respuesta técnica también depende del hecho de que, en este caso, el código después del "retorno", si se omite, no producirá un efecto secundario. JavaScript se dividirá felizmente por cero y devolverá + Infinito / -Infinito o NaN.