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 reject
para salir temprano, ¿debo adquirir el hábito de hacerlo también return
inmediatamente después?
Respuestas:
El
return
propósito 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.En este caso, evita
resolve(numerator / denominator);
que se ejecute, 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:
Cuando una promesa se cumple o se rechaza, 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.
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:
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:
Este es el flujo de control estándar de JS.
resolve
/reject
:Mostrar fragmento de código
resolve
/reject
- ya 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:Mostrar fragmento de código
Mostrar fragmento de código
Prefiero usar una de las
return
opciones ya que el código es más plano.fuente
return
está allí o no, porque una vez que se ha establecido un estado de promesa, no se puede cambiar, por lo que llamarresolve()
después de llamarreject()
no hará nada excepto usar unos pocos ciclos de CPU adicionales. Yo mismo usaría elreturn
punto de vista de la limpieza y la eficiencia del código justo, pero no es obligatorio en este ejemplo específico.Promise.try(() => { })
lugar de una nueva Promesa y evite usar llamadas de resolución / rechazo. En su lugar, podría escribirreturn denominator === 0 ? throw 'Cannot divide by zero' : numerator / denominator;
I usePromise.try
como medio para iniciar una Promesa y eliminar promesas envueltas en bloques try / catch que son problemáticos.reject
eso que hace algo costoso, como conectarse a bases de datos o puntos finales API? Todo sería innecesario y le costaría dinero y recursos, especialmente, por ejemplo, si se conecta a algo como una base de datos de AWS o un punto final de API Gateway. En ese caso, definitivamente usaría un retorno para evitar que se ejecute código innecesario.return
.Un modismo común, que puede o no ser su taza de té, es combinar el
return
con elreject
, rechazar simultáneamente la promesa y salir de la función, de modo que el resto de la función, incluido elresolve
no se ejecute. Si te gusta este estilo, puede hacer que tu código sea un poco más compacto.Esto funciona bien porque el constructor promesa no hace nada con cualquier valor de retorno, y en todo caso
resolve
yreject
de retorno nada.El mismo idioma se puede usar con el estilo de devolución de llamada que se muestra en otra respuesta:
Nuevamente, esto funciona bien porque la persona que llama
divide
no espera que devuelva nada y no hace nada con el valor de retorno.fuente
reject
estadoTé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 llamar exactamente a 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.
fuente
Si no "regresa" después de una resolución / rechazo, pueden suceder cosas malas (como una redirección de página) después de que haya querido que se detuviera. Fuente: me encontré con esto.
fuente
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 el uso
return
temprano también es muy común en las devoluciones de llamada:Entonces, si bien es una buena práctica en las promesas, se requiere con devoluciones de llamada. Algunas notas sobre su código:
fuente
En muchos casos, es posible validar los parámetros por separado e inmediatamente devolver una promesa rechazada con Promise.reject (motivo) .
fuente