Suponha que eu tenha o seguinte código.
function divide(numerator, denominator) {
return new Promise((resolve, reject) => {
if(denominator === 0){
reject("Cannot divide by 0");
return; //superfluous?
}
resolve(numerator / denominator);
});
}
Se meu objetivo é reject
sair mais cedo, devo adquirir o hábito de também return
imediatamente depois?
Respostas:
O
return
objetivo é encerrar a execução da função após a rejeição e impedir a execução do código após ela.Nesse caso, impede a
resolve(numerator / denominator);
execução, o que não é estritamente necessário. No entanto, ainda é preferível encerrar a execução para evitar uma possível interceptação no futuro. Além disso, é uma boa prática evitar a execução desnecessária de código.fundo
Uma promessa pode estar em um dos três estados:
Quando uma promessa é cumprida ou rejeitada, ela permanece neste estado indefinidamente (liquidada). Portanto, rejeitar uma promessa cumprida ou cumprir uma promessa rejeitada não terá efeito.
Este trecho de exemplo mostra que, embora a promessa tenha sido cumprida após ser rejeitada, ela permaneceu rejeitada.
Então, por que precisamos retornar?
Embora não possamos alterar um estado de promessa estabelecida, rejeitar ou resolver não interromperá a execução do restante da função. A função pode conter código que criará resultados confusos. Por exemplo:
Mesmo que a função não contenha esse código no momento, isso cria uma possível armadilha futura. Um refatorador futuro pode ignorar o fato de que o código ainda é executado após a promessa ser rejeitada e será difícil depurar.
Parando a execução após resolver / rejeitar:
Esse é o material padrão do fluxo de controle JS.
resolve
/reject
:Mostrar snippet de código
resolve
/reject
- como o valor de retorno do retorno de chamada é ignorado, podemos salvar uma linha retornando a instrução rejeitar / resolver:Mostrar snippet de código
Mostrar snippet de código
Prefiro usar uma das
return
opções, pois o código é mais plano.fonte
return
estiver lá ou não, porque uma vez que um estado de promessa foi definido, ele não poderá ser alterado, portanto, a chamadaresolve()
após a chamadareject()
não fará nada, exceto usar alguns ciclos extras da CPU. Eu próprio usaria oreturn
just do ponto de vista de limpeza e eficiência do código, mas não é necessário neste exemplo específico.Promise.try(() => { })
vez da nova promessa e evite usar chamadas de resolver / rejeitar. Em vez disso, você pode escrever quereturn denominator === 0 ? throw 'Cannot divide by zero' : numerator / denominator;
eu usoPromise.try
como meio de iniciar uma promessa e eliminar promessas envolvidas em blocos try / catch que são problemáticos.reject
que faz algo caro, como conectar-se a bancos de dados ou pontos finais de API? Tudo isso seria desnecessário e custaria dinheiro e recursos, especialmente por exemplo, se você estiver se conectando a algo como um banco de dados da AWS ou um ponto de extremidade do API Gateway. Nesse caso, você definitivamente usaria um retorno para evitar que códigos desnecessários fossem executados.return
.Um idioma comum, que pode ou não ser sua xícara de chá, é combinar o
return
com oreject
, para rejeitar simultaneamente a promessa e sair da função, para que o restante da função, incluindo o,resolve
não seja executado. Se você gosta desse estilo, ele pode tornar seu código um pouco mais compacto.Isso funciona bem porque o construtor Promise não faz nada com nenhum valor de retorno e, em qualquer caso,
resolve
ereject
não retorna nada.O mesmo idioma pode ser usado com o estilo de retorno de chamada mostrado em outra resposta:
Novamente, isso funciona bem porque a pessoa que
divide
está chamando não espera que retorne nada e não faz nada com o valor de retorno.fonte
reject
statusTecnicamente , não é necessário aqui 1 - porque uma Promessa pode ser resolvida ou rejeitada, exclusivamente e apenas uma vez. O primeiro resultado da promessa vence e todos os resultados subsequentes são ignorados. Isso é diferente dos retornos de chamada no estilo do nó.
Dito isto, é uma boa prática limpa garantir que exatamente um seja chamado, quando prático, e, de fato, neste caso, uma vez que não há mais processamento assíncrono / adiado. A decisão de "retornar cedo" não é diferente de terminar qualquer função quando seu trabalho estiver concluído - em vez de continuar o processamento não relacionado ou desnecessário.
Retornar no momento apropriado (ou usar condicionais para evitar a execução do "outro caso") reduz a chance de executar código acidentalmente em um estado inválido ou de executar efeitos colaterais indesejados; e, como tal, torna o código menos propenso a 'quebrar inesperadamente'.
1 Essa resposta tecnicamente também depende do fato de que , neste caso, o código após o "retorno", caso seja omitido, não resultará em um efeito colateral. Felizmente , o JavaScript será dividido por zero e retornará + Infinity / -Infinity ou NaN.
fonte
Se você não "retornar" após uma resolução / rejeição, coisas ruins (como um redirecionamento de página) poderão acontecer depois que você tiver a intenção de parar. Fonte: Corri para isso.
fonte
A resposta de Ori já explica que não é necessário,
return
mas é uma boa prática. Observe que o construtor de promessas é seguro, portanto ignorará as exceções lançadas passadas posteriormente no caminho. Essencialmente, você tem efeitos colaterais que não podem ser observados facilmente.Observe que o
return
início precoce também é muito comum em retornos de chamada:Portanto, embora seja uma boa prática em promessas, é necessário com retornos de chamada. Algumas notas sobre o seu código:
fonte
Em muitos casos, é possível validar parâmetros separadamente e retornar imediatamente uma promessa rejeitada com Promise.reject (reason) .
fonte