Independentemente do seu nível de habilidade, erros ou exceções fazem parte da vida do seu desenvolvedor de aplicativos. A inconsistência do desenvolvimento da Web deixa muitos lugares onde os erros podem e aconteceram. A chave para a solução é lidar com qualquer erro imprevisto (ou previsível) para controlar a experiência do usuário. Com o JavaScript, há uma variedade de recursos técnicos e de idioma que podem ser usados para resolver corretamente qualquer problema.
É perigoso lidar com erros no JavaScript. Se você acredita na lei de Murphy, o que vai dar errado acabará por dar errado! Neste artigo, vou investigar o tratamento de erros no JavaScript. Envolverei algumas armadilhas e boas práticas. Finalmente, discutiremos o processamento de código assíncrono e o Ajax.
Eu acho que o modelo de JavaScript orientado a eventos acrescenta um significado rico a esse idioma. Eu acho que não há diferença entre o mecanismo orientado a eventos e o mecanismo de relatório de erros desse tipo de navegador. Sempre que ocorre um erro, é equivalente a lançar um evento em um determinado momento. Em teoria, podemos lidar com erros de lançamento de eventos em JavaScript, como lidamos com eventos comuns. Se isso parece estranho para você, concentre -se em iniciar a jornada abaixo. Este artigo tem como alvo apenas o JavaScript do cliente.
Exemplo
Os exemplos de código usados neste artigo podem ser obtidos no GitHub. A página atual se parece com o seguinte:
Clicar em cada botão lançará um erro. Ele simula a exceção do tipo TypeError. A seguir, é apresentada a definição e o teste de unidade de um módulo.
função error () {var foo = {}; retornar foo.bar ();}Primeiro, esta função define um objeto vazio Foo. Observe que o método bar () não está definido em nenhum lugar. Usamos testes de unidade para verificar se isso lança um erro.
It ('lança um tipoError', function () {deve.THOWS (Target, TypeError);});Este teste de unidade usa asserções de teste das bibliotecas Mocha e deve.js. O Mocha é uma estrutura de teste em execução e deve ser uma biblioteca de asserção. Se você não estiver muito familiarizado com eles, poderá procurar seus documentos on -line gratuitamente. Um caso de teste geralmente começa com ele ('Descrição') e termina com a passagem ou a falha da afirmação deve. A vantagem de usar essa estrutura é que ela pode ser testada na unidade no nó, e não no navegador. Eu sugiro que você leve esses testes a sério porque eles validam muitos conceitos básicos importantes em JavaScript.
Como mostrado acima, o erro () define um objeto vazio e, em seguida, tenta chamar o método nele. Como o método bar () não existe nesse objeto, ele lançará uma exceção. Confie em mim, em idiomas dinâmicos como JavaScript, qualquer um pode cometer esses erros.
Más demonstração
Vamos dar uma olhada nos más métodos de manuseio de erros. Vou abstrair a ação errada e vinculá -la ao botão. Aqui está como é o teste de unidade do manipulador:
função badhandler (fn) {tente {return fn (); } catch (e) {} retornar null;}Essa função de processamento recebe uma função de retorno de chamada FN como uma dependência. Então a função é chamada dentro do manipulador. Estes exemplos de teste de unidade como usar esse método.
ele ('retorna um valor sem erros', function () {var fn = function () {return 1;}; var resultado = alvo (fn); resultado. deve (resultado) .Equal (nulo);});Como você pode ver, se ocorrer um erro, essa maneira estranha de lidar devolve um nulo. Esta função de retorno de chamada fn () apontará para um método ou erro legal. O evento de processamento de clique abaixo completa o resto.
(function (manipulador, bomba) {var badbutton = document.getElementById ('Bad'); if (Badbutton) {Badbutton.addeventListener ('Click', function () {Handler (bomba); Console.log ('Imagine, sendo promovido a ocultar erros');});};O ruim é que o que eu acabei de receber foi um nulo. Isso me deixou muito confuso quando eu estava pensando em determinar o que estava errado. Essa estratégia de silêncio quando ocorre os erros cobre todos os links do design da experiência do usuário à corrupção de dados. O lado frustrante se seguiu foi que eu tinha que passar horas de depuração, mas não conseguia ver o erro no bloco de código de Try-Catch. Esse processamento estranho oculta todos os erros no código e pressupõe que tudo esteja normal. Isso pode ser executado sem problemas em algumas equipes que não prestam atenção à qualidade do código. No entanto, esses erros ocultos eventualmente o forçarão a gastar horas depurando seu código. Em uma solução de várias camadas que depende da pilha de chamadas, é possível determinar de onde vem o erro. Pode ser apropriado manusear silenciosamente a captura de tentativa em casos raros. Mas se você encontrar um erro, lidará com isso, o que não é uma boa solução.
Essa falha-isso é uma estratégia de silêncio solicitará que você lide melhor com erros em seu código. O JavaScript fornece uma maneira mais elegante de lidar com esse tipo de problema.
Solução não legível
Vamos continuar, vamos dar uma olhada na abordagem difícil de entender. Vou pular a parte firmemente acoplada ao DOM. Esta parte não é diferente da abordagem ruim que acabamos de ver. O foco está na parte do teste de unidade abaixo que lida com exceções.
função uglyhandler (fn) {tente {return fn (); } catch (e) {tiro erro ('um novo erro'); }} it ('Retorna um novo erro com erros', function () {var fn = function () {throw new typeError ('tipo error');};Há uma boa melhoria em comparação com o tratamento ruim agora. A exceção é lançada na pilha de chamadas. O que eu gosto é que os erros são libertados da pilha, o que é extremamente útil para a depuração. Quando uma exceção é lançada, o intérprete analisará a próxima função de processamento em um nível na pilha de chamadas. Isso oferece muitas oportunidades para lidar com erros no nível superior da pilha de chamadas. Infelizmente, como ele é um erro difícil, não consigo ver as informações originais do erro. Então, eu tenho que olhar ao longo da pilha de chamadas e encontrar a exceção mais primitiva. Mas pelo menos eu sei que há um erro acontecendo onde a exceção é lançada.
Embora esse manuseio de erros ilegível seja inócuo, torna o código difícil de entender. Vamos ver como o navegador lida com erros.
Ligue para a pilha
Em seguida, uma maneira de lançar uma exceção é adicionar uma tentativa ... Captura do bloco de código no nível superior da pilha de chamadas. Por exemplo:
função main (bomba) {try {bomba (); } catch (e) {// lida com todas as coisas de erro}}Mas lembra que eu disse que os navegadores são orientados a eventos? Sim, uma exceção no JavaScript nada mais é do que um evento. O intérprete interrompe o programa no contexto em que a exceção está ocorrendo atualmente e lança uma exceção. Para confirmar isso, a seguir é uma função de manuseio de eventos globais que podemos ver. Parece o seguinte:
window.addeventListener ('erro', function (e) {var error = e.error; console.log (erro);});Este manipulador de eventos captura erros no ambiente de execução. Os eventos de erro produzirão vários erros em vários lugares. O objetivo dessa abordagem é lidar centralmente em erros no código. Assim como outros eventos, você pode usar um manipulador global para lidar com vários erros. Isso faz com que o manuseio de erros seja apenas um objetivo se você aderir à sólida (responsabilidade única, substituição de liskov, fechada, liskov, segregação de interface e inversão de dependência). Você pode registrar funções de manuseio de erros a qualquer momento. O intérprete executa esses loops de funções. O código é libertado de declarações cheias de tentativa ... captura e fica fácil de depurar. A chave para essa abordagem é lidar com erros que ocorrem como eventos JavaScript normais.
Agora, existe uma maneira de exibir a pilha de chamadas com uma função de processamento global. O que podemos fazer com isso? Afinal, temos que usar a pilha de chamadas.
Grave a pilha de chamadas
A pilha de chamadas é muito útil no manuseio de correções de erros. A boa notícia é que o navegador fornece essas informações. Embora o atributo da pilha do objeto de erro não seja padrão no momento, esse atributo geralmente é suportado em navegadores mais recentes.
Então, a coisa legal que podemos fazer é imprimi -lo para o servidor:
window.adDeventListener ('erro', function (e) {var stack = e.error.stack; var message = e.error.toString (); if (Stack) {message + = '/n' + pilha;} var xhr = new XmlHtPrequest (); xhr.open ('' ',' ''/);Pode não ser óbvio no exemplo do código, mas esse manipulador de eventos será acionado pelo código de erro anterior. Como mencionado acima, cada manipulador tem um único objetivo, o que torna o código secar (não se repita sem fazer a roda repetidamente). O que estou interessado é como capturar essas mensagens no servidor.
Aqui está uma captura de tela do Node Runtime:
A pilha de chamadas é muito útil para depurar o código. Nunca subestime o papel da pilha de chamadas.
Processamento assíncrono
Oh, é bastante perigoso lidar com o código assíncrono! O JavaScript traz código assíncrono do ambiente atual de execução. Isso significa que há um problema com a declaração de tentativa ... Catch abaixo.
função assynchandler (fn) {try {setTimeout (function () {fn ();}, 1); } catch (e) {}}Ainda há alguma parte restante deste teste de unidade:
ele ('não captura exceções com erros', function () {var fn = function () {lança novo TypeError ('Erro de tipo');}; falha na falha (function () {target (fn);}). });}Eu tenho que encerrar esse manipulador com a promessa de verificar a exceção. Observe que, embora meu código esteja em tentativa ... Catch, uma exceção não atendida ainda aparece. Sim, tente ... o captura só funciona em um ambiente de execução separado. Quando a exceção é lançada, o ambiente de execução do intérprete não é mais o bloco de tentativa atual. Esse comportamento ocorre de maneira semelhante às chamadas do Ajax. Então, agora existem duas opções. Uma alternativa é capturar exceções em um retorno de chamada assíncrona:
setTimeout (function () {try {fn ();} catch (e) {// lidera este erro assíncrono}}, 1);Embora esse método seja útil, ainda há muito espaço para melhorias. Primeiro, tente ... os blocos de código de captura aparecem em todos os lugares do código. De fato, na década de 1970, as chamadas de programação, eles queriam que seu código recuasse. Além disso, o mecanismo V8 desencoraja o uso de blocos de código de pegador… Catch nas funções (V8 é o mecanismo JavaScript usado pelo navegador Chrome e pelo nó). Eles recomendam escrever esses blocos de código que capturam exceções na parte superior da pilha de chamadas.
Então, o que isso nos diz? Como eu disse acima, os manipuladores de erros globais em qualquer contexto de execução são necessários. Se você adicionar um manipulador de erros a um objeto de janela, ou seja, terminou! Não é bom seguir os princípios de seco e sólido? Um manipulador de erros global manterá seu código legível e limpo.
A seguir, é apresentado o relatório impresso no manuseio de exceções do lado do servidor. Observe que, se você usar o código no exemplo, a saída poderá variar um pouco, dependendo do navegador que você está usando.
Este manipulador pode até me dizer qual erro vem do código assíncrono. Ele me diz que o erro vem do manipulador setTimeout (). Tão legal!
Os erros fazem parte de todos os aplicativos, mas o manuseio adequado de erros não é. Existem pelo menos duas maneiras de lidar com erros. Uma é uma solução de falha, silêncio, ou seja, ignorar erros no código. Outra maneira é detectar e resolver rapidamente erros, ou seja, parar no erro e se reproduzir. Acho que expressei claramente o que gosto e por que gosto. Minha escolha: não esconda o problema. Ninguém o culpará por eventos inesperados em seu programa. Isso é aceitável, para quebrar pontos, reproduzir e experimentar o usuário. Em um mundo imperfeito, é importante ter uma chance. Os erros são inevitáveis e o que você faz é importante para resolver o erro. O uso razoável dos recursos de manuseio de erros do JavaScript e decodificação automática e flexível pode facilitar a experiência do usuário e também facilitar o diagnóstico do desenvolvedor.