Digamos que eu tenho essa função de autenticação que retorna uma promessa. A promessa então se resolve com o resultado. Falso e verdadeiro são resultados esperados, a meu ver, e as rejeições devem ocorrer apenas em caso de erro. Ou, uma falha na autenticação é considerada algo que você rejeitaria uma promessa?
javascript
Mathieu Bertin
fonte
fonte
reject
e não deve retornar falso, mas se espera que o valor seja aBool
, teve êxito e deve resolver com o Bool, independentemente do valor. As promessas são uma espécie de proxies para valores - elas armazenam o valor retornado, portanto, somente se o valor não puder ser obtido, você devereject
. Caso contrário, você deveriaresolve
.false
ou lançando uma exceção?then
quando o servidor responde - mesmo que um código de erro seja retornado - e você deve verificar oresponse.ok
. Ocatch
manipulador é acionado apenas para erros inesperados .Respostas:
Boa pergunta! Não há resposta difícil. Depende do que você considera ser excepcional em que ponto específico do fluxo .
Rejeitar a
Promise
é o mesmo que criar uma exceção. Nem todos os resultados indesejados são excepcionais , o resultado de erros . Você poderia discutir seu caso de duas maneiras:Falha de autenticação deve
reject
aPromise
, porque o chamador está esperando umUser
objeto em troca, e qualquer outra coisa é uma exceção a esse fluxo.Falha de autenticação deve
resolve
aPromise
, ainda quenull
, uma vez de fornecer as credenciais erradas não é realmente um excepcional caso, e o chamador não deve esperar o fluxo para sempre resultam em umUser
.Observe que estou analisando o problema do lado do chamador . No fluxo de informações, o chamador espera que suas ações resultem em um
User
(e qualquer outra coisa é um erro) ou faz sentido que esse chamador em particular lide com outros resultados?Em um sistema de várias camadas, a resposta pode mudar à medida que os dados fluem pelas camadas. Por exemplo:
null
usuário.Este exemplo de 4 pontos é obviamente complicado, mas ilustra 2 pontos:
Então, novamente, nenhuma resposta difícil. Hora de pensar e projetar!
fonte
Portanto, o Promises tem uma boa propriedade que eles trazem JS de linguagens funcionais, ou seja, eles realmente implementam esse
Either
construtor de tipos que cola dois outros tipos, oLeft
tipo e oRight
tipo, forçando a lógica a assumir uma ramificação ou outra ramo.Agora você está percebendo que o tipo do lado esquerdo é ambíguo para promessas; você pode rejeitar com qualquer coisa. Isso ocorre porque o JS é fracamente digitado, mas você deve ser cauteloso se estiver programando defensivamente.
O motivo é que o JS também recebe
throw
instruções do código de tratamento de promessas e o agrupa noLeft
lado dele. Tecnicamente, em JS, você pode dethrow
tudo, incluindo verdadeiro / falso, uma sequência ou um número: mas o código JavaScript também lança coisas semthrow
(quando você faz coisas como tentar acessar propriedades em valores nulos) e existe uma API estabelecida para isso (oError
objeto) . Portanto, quando você tenta capturar, geralmente é bom poder assumir que esses erros sãoError
objetos. E como areject
promessa de aglomeração de erros de qualquer um dos erros acima, geralmente você deseja apenasthrow
outros erros, para fazer com que suacatch
declaração tenha uma lógica simples e consistente.Portanto, embora você possa colocar um if-condicional no seu
catch
e procurar por erros falsos, nesse caso, o caso da verdade é trivial,você provavelmente preferirá a estrutura lógica, pelo menos para o que sai imediatamente do autenticador, de um booleano mais simples:
De fato, o próximo nível de lógica de autenticação é provavelmente retornar algum tipo de
User
objeto que contém o usuário autenticado, para que isso se torne:e isso é mais ou menos o que eu esperaria: retornar
null
no caso em que o usuário não estiver definido, caso contrário, retorne{user_id: <number>, permission_to_launch_missiles: <boolean>}
. Eu esperaria que o caso geral de não estar logado seja recuperável, por exemplo, se estivermos em algum tipo de modo "demo para novos clientes" e não deva ser misturado com bugs nos quais chamei acidentalmenteobject.doStuff()
quandoobject.doStuff
estavaundefined
.Agora com o que disse, o que você pode querer fazer é definir um
NotLoggedIn
ouPermissionError
exceção que derivaError
. Então, nas coisas que realmente precisam, você deseja escrever:fonte
Erros
Vamos falar sobre erros.
Existem dois tipos de erros:
Erros esperados
Os erros esperados são estados em que a coisa errada acontece, mas você sabe que pode, então lida com isso.
São coisas como entrada do usuário ou solicitações do servidor. Você sabe que o usuário pode cometer um erro ou que o servidor pode estar inoperante; portanto, escreva algum código de verificação para garantir que o programa solicite a entrada novamente ou exiba uma mensagem ou qualquer outro comportamento apropriado.
Estes são recuperáveis quando manuseados. Se deixados sem tratamento, eles se tornam erros inesperados.
Erros inesperados
Erros inesperados (bugs) são estados em que a coisa errada acontece porque o código está errado. Você sabe que eles acabarão por acontecer, mas não há como saber onde ou como lidar com eles, porque, por definição, são inesperados.
São coisas como erros de sintaxe e de lógica. Você pode ter um erro de digitação no seu código, pode ter chamado uma função com os parâmetros incorretos. Normalmente não são recuperáveis.
try..catch
Vamos conversar
try..catch
.Em JavaScript,
throw
não é comumente usado. Se você procurar exemplos em código, eles serão poucos e distantes entre si, e geralmente estruturados de acordo com as linhas dePor esse
try..catch
motivo , os blocos também não são tão comuns no fluxo de controle. Geralmente, é muito fácil adicionar algumas verificações antes de chamar métodos para evitar erros esperados.Os ambientes JavaScript também são bastante indulgentes; portanto, erros inesperados também costumam ser detectados.
C # :try..catch
não precisa ser incomum. Existem alguns casos de uso interessantes, que são mais comuns em linguagens como Java e C #. Java e C # têm a vantagem decatch
construções digitadas , para que você possa diferenciar entre erros esperados e inesperados:Este exemplo permite que outras exceções inesperadas fluam e sejam tratadas em outro lugar (como sendo registrado e fechando o programa).
Em JavaScript, essa construção pode ser replicada via:
Não é tão elegante, o que é parte da razão pela qual é incomum.
Funções
Vamos falar sobre funções.
Se você usar o princípio da responsabilidade única , cada classe e função deve servir a um propósito singular.
Por exemplo,
authenticate()
pode autenticar um usuário.Isso pode ser escrito como:
Como alternativa, pode ser escrito como:
Ambos são aceitáveis.
Promessas
Vamos falar de promessas.
Promessas são uma forma assíncrona de
try..catch
. Chamandonew Promise
ouPromise.resolve
inicia seutry
código. Ligarthrow
ouPromise.reject
enviar você para ocatch
código.Se você possui uma função assíncrona para autenticar um usuário, pode escrevê-la como:
Como alternativa, pode ser escrito como:
Ambos são aceitáveis.
Aninhamento
Vamos falar sobre aninhamento.
try..catch
pode ser aninhado. Seuauthenticate()
método pode ter internamente umtry..catch
bloco como:Da mesma forma, as promessas podem ser aninhadas. Seu
authenticate()
método assíncrono pode usar internamente promessas:Então qual é a resposta?
Ok, acho que é hora de eu realmente responder à pergunta:
A resposta mais simples que posso dar é que você deve rejeitar uma promessa em qualquer lugar que desejaria
throw
uma exceção, se fosse um código síncrono.Se seu fluxo de controle for mais simples, com algumas
if
verificações em suasthen
declarações, não há necessidade de rejeitar uma promessa.Se o seu fluxo de controle for mais simples, rejeitando uma promessa e, em seguida, verificando tipos de erros no seu código de tratamento de erros, faça-o.
fonte
Eu usei o ramo "rejeitar" de uma promessa para representar a ação "cancelar" das caixas de diálogo jQuery UI. Parecia mais natural do que usar o ramo "resolver", principalmente porque geralmente há várias opções de "fechamento" em uma caixa de diálogo.
fonte
Lidar com uma promessa é mais ou menos como a condição "se". Cabe a você decidir se deseja "resolver" ou "rejeitar" se a autenticação falhar.
fonte
try..catch
, nãoif
.try...catch
e simplesmente dizer que, se conseguir concluir e obter um resultado, deverá resolver independentemente do valor recebido, caso contrário você deve rejeitar?try { if (!doSomething()) throw whatever; doSomethingElse() } catch { ... }
é perfeitamente bom, mas a construção que aPromise
representa é atry..catch
parte, não aif
parte.doSomething()
falhar, será lançada, mas, se não, poderá conter o valor que você precisa (o que você disseif
acima é um pouco confuso, pois não faz parte da sua ideia aqui :)). Você só deve rejeitar se houver um motivo para lançar (na analogia), portanto, se o teste falhar. Se o teste for bem-sucedido, você sempre deve resolver, independentemente de seu valor ser positivo, certo?