Este README contém informações que aprendi ao longo dos anos sobre como lidar com erros de JavaScript, relatá-los ao servidor e navegar por vários bugs que podem tornar tudo isso realmente difícil. Os navegadores melhoraram nessa área, mas ainda há espaço para melhorias para garantir que todos os aplicativos possam lidar de maneira sensata e sólida com qualquer erro que aconteça.
Os casos de teste do conteúdo encontrado neste guia podem ser encontrados em https://mknichel.github.io/javascript-errors/.
Índice
Introdução
Anatomia de um erro de JavaScript
Produzindo um erro de JavaScript
Mensagens de erro
Formato de rastreamento de pilha
Capturando erros de JavaScript
janela.onerror
tentar/pegar
Pontos de entrada protegidos
Promessas
Trabalhadores da Web
Extensões do Chrome
Capturar, relatar e corrigir erros é uma parte importante de qualquer aplicativo para garantir a integridade e a estabilidade do aplicativo. Como o código JavaScript também é executado no cliente e em muitos ambientes de navegador diferentes, também pode ser difícil ficar atento aos erros JS do seu aplicativo. Não há especificações formais da web sobre como relatar erros de JS que causam diferenças na implementação de cada navegador. Além disso, houve muitos bugs na implementação de erros de JavaScript nos navegadores que tornaram isso ainda mais difícil. Esta página navega por esses aspectos dos erros JS para que futuros desenvolvedores possam lidar melhor com os erros e esperamos que os navegadores convirjam para soluções padronizadas.
Um erro de JavaScript é composto de duas partes principais: a mensagem de erro e o rastreamento de pilha . A mensagem de erro é uma string que descreve o que deu errado, e o rastreamento de pilha descreve onde o erro ocorreu no código. Erros JS podem ser produzidos pelo próprio navegador ou lançados pelo código do aplicativo.
Um erro JS pode ser gerado pelo navegador quando um trecho de código não é executado corretamente ou pode ser gerado diretamente pelo código.
Por exemplo:
var a = 3;a();
Neste exemplo, uma variável que na verdade é um número não pode ser invocada como uma função. O navegador gerará um erro como TypeError: a is not a function
with a stack trace que aponta para essa linha de código.
Um desenvolvedor também pode querer lançar um erro em um trecho de código se uma determinada pré-condição não for atendida. Por exemplo
if (!checkPrecondition()) { throw new Error("Não atende à pré-condição!");}
Neste caso, o erro será Error: Doesn't meet precondition!
. Este erro também conterá um rastreamento de pilha que aponta para a linha apropriada. Erros gerados pelo navegador e pelo código do aplicativo podem ser tratados da mesma forma.
Existem várias maneiras pelas quais os desenvolvedores podem gerar um erro no JavaScript:
throw new Error('Problem description.')
throw Error('Problem description.')
<- equivalente ao primeiro
throw 'Problem description.'
<-- ruim
throw null
<- ainda pior
Lançar uma string ou nulo realmente não é recomendado, pois o navegador não anexará um rastreamento de pilha a esse erro, perdendo o contexto de onde o erro ocorreu no código. É melhor lançar um objeto Error real, que conterá a mensagem de erro, bem como um rastreamento de pilha que aponta para as linhas corretas de código onde o erro ocorreu.
Cada navegador tem seu próprio conjunto de mensagens que usa para as exceções integradas, como o exemplo acima para tentar chamar uma não função. Os navegadores tentarão usar as mesmas mensagens, mas como não há especificações, isso não é garantido. Por exemplo, tanto o Chrome quanto o Firefox usam {0} is not a function
para o exemplo acima, enquanto o IE11 reportará Function expected
(principalmente sem relatar qual variável tentou ser chamada).
No entanto, os navegadores também tendem a divergir com frequência. Quando houver várias instruções padrão em uma instrução switch
, o Chrome lançará "More than one default clause in switch statement"
enquanto o Firefox reportará "more than one switch default"
. À medida que novos recursos são adicionados à web, essas mensagens de erro precisam ser atualizadas. Essas diferenças podem surgir mais tarde, quando você estiver tentando lidar com erros relatados de código ofuscado.
Você pode encontrar os modelos que os navegadores usam para mensagens de erro em:
Raposa de fogo - http://mxr.mozilla.org/mozilla1.9.1/source/js/src/js.msg
Chrome - https://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/messages.js
Internet Explorer - https://github.com/Microsoft/ChakraCore/blob/4e4d4f00f11b2ded23d1885e85fc26fcc96555da/lib/Parser/rterrors.h
Os navegadores produzirão diferentes mensagens de erro para algumas exceções.
O rastreamento de pilha é uma descrição de onde ocorreu o erro no código. É composto por uma série de frames, onde cada frame descreve uma linha específica do código. O quadro superior é o local onde o erro foi lançado, enquanto os quadros subsequentes são a pilha de chamadas de função - ou como o código foi executado para chegar ao ponto onde o erro foi lançado. Como o JavaScript geralmente é concatenado e reduzido, também é importante ter números de coluna para que a instrução exata possa ser localizada quando uma determinada linha tiver uma infinidade de instruções.
Um rastreamento de pilha básico no Chrome se parece com:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
Cada quadro de pilha consiste em um nome de função (se aplicável e o código não foi executado no escopo global), o script de onde veio e o número da linha e da coluna do código.
Infelizmente, não existe um padrão para o formato de rastreamento de pilha, portanto isso varia de acordo com o navegador.
O rastreamento de pilha do Microsoft Edge e do IE 11 é semelhante ao do Chrome, exceto que lista explicitamente o código global:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:3) at Global code (http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3)
O rastreamento de pilha do Firefox é semelhante a:
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9 @http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
O formato do Safari é semelhante ao formato do Firefox, mas também é um pouco diferente:
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:18 global code@http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:13
As mesmas informações básicas estão lá, mas o formato é diferente.
Observe também que no exemplo do Safari, além do formato ser diferente do Chrome, os números das colunas são diferentes do Chrome e do Firefox. Os números das colunas também podem divergir mais em diferentes situações de erro - por exemplo, no código (function namedFunction() { throwError(); })();
, o Chrome reportará a coluna para a chamada da função throwError()
enquanto o IE11 reportará o número da coluna como o início da string. Essas diferenças voltarão a ocorrer mais tarde, quando o servidor precisar analisar o rastreamento de pilha em busca de erros relatados e desofuscar rastreamentos de pilha ofuscados.
Consulte https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack para obter mais informações sobre a propriedade stack dos erros. Ao acessar a propriedade Error.stack, o Chrome inclui a mensagem de erro como parte da pilha, mas o Safari 10+ não.
O formato dos rastreamentos de pilha é diferente de acordo com o navegador no formato e nos números das colunas usadas.
Indo mais fundo, há muitas nuances nos formatos de rastreamento de pilha que são discutidas nas seções abaixo.
Por padrão, as funções anônimas não têm nome e aparecem como uma string vazia ou como "Função anônima" nos nomes das funções no rastreamento de pilha (dependendo do navegador). Para melhorar a depuração, você deve adicionar um nome a todas as funções para garantir que elas apareçam no stack frame. A maneira mais fácil de fazer isso é garantir que as funções anônimas sejam especificadas com um nome, mesmo que esse nome não seja usado em nenhum outro lugar. Por exemplo:
setTimeout(função nomeOfTheAnonymousFunction() { ... }, 0);
Isso fará com que o rastreamento de pilha vá de:
at http://mknichel.github.io/javascript-errors/javascript-errors.js:125:17
para
at nameOfTheAnonymousFunction (http://mknichel.github.io/javascript-errors/javascript-errors.js:121:31)
No Safari, isso iria de:
https://mknichel.github.io/javascript-errors/javascript-errors.js:175:27
para
nameOfTheAnonymousFunction@https://mknichel.github.io/javascript-errors/javascript-errors.js:171:41
Este método garante que nameOfTheAnonymousFunction
apareça no quadro para qualquer código dentro dessa função, tornando a depuração muito mais fácil. Consulte http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/#toc-debugging-tips para obter mais informações.
Os navegadores também usarão o nome da variável ou propriedade à qual uma função está atribuída se a função em si não tiver um nome. Por exemplo, em
var fnNomeVariável = function() { ... };
os navegadores usarão fnVariableName
como o nome da função nos rastreamentos de pilha.
at throwError (http://mknichel.github.io/javascript-errors/javascript-errors.js:27:9) at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
Ainda mais sutil do que isso, se esta variável for definida dentro de outra função, todos os navegadores usarão apenas o nome da variável como o nome da função no rastreamento de pilha, exceto o Firefox, que usará uma forma diferente que concatena o nome de a função externa com o nome da variável interna. Exemplo:
função throwErrorFromInnerFunctionAssignedToVariable() { var fnNomeVariável = function() { throw new Error("foo"); }; fnNomeVariável();}
produzirá no Firefox:
throwErrorFromInnerFunctionAssignedToVariable/fnVariableName@http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37
Em outros navegadores, seria assim:
at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
O Firefox usa texto de quadro de pilha diferente para funções definidas em outra função.
O nome de exibição de uma função também pode ser definido pela propriedade displayName
em todos os principais navegadores, exceto no IE11. Nestes navegadores, o displayName aparecerá no depurador devtools, mas em todos os navegadores, exceto Safari, ele não será usado em rastreamentos de pilha de erros (o Safari difere dos demais por também usar o displayName no rastreamento de pilha associado a um erro).
var someFunction = function() {};someFunction.displayName = " # Uma descrição mais longa da função.";
Não há especificações oficiais para a propriedade displayName, mas ela é suportada por todos os principais navegadores. Consulte https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName e http://www.alertdebugging.com/2009/04/29/building-a-better -javascript-profiler-with-webkit/ para obter mais informações sobre displayName.
O IE11 não oferece suporte à propriedade displayName.
O Safari usa a propriedade displayName como o nome do símbolo nos rastreamentos de pilha de erros.
Se um erro for relatado sem um rastreamento de pilha (veja mais detalhes sobre quando isso aconteceria abaixo), será possível capturar programaticamente um rastreamento de pilha.
No Chrome, isso é realmente fácil de fazer usando a API Error.captureStackTrace
. Consulte https://github.com/v8/v8/wiki/Stack%20Trace%20API para obter mais informações sobre o uso desta API.
Por exemplo:
função ignoreThisFunctionInStackTrace() { var err = novo erro(); Error.captureStackTrace(err, ignoreThisFunctionInStackTrace); retornar err.stack;}
Em outros navegadores, um rastreamento de pilha também pode ser coletado criando um novo erro e acessando a propriedade stack desse objeto:
var err = new Error('');return err.stack;
No entanto, o IE10 preenche apenas o rastreamento de pilha quando o erro é realmente gerado:
tentar { lançar novo erro ('');} catch (e) { retornar e.stack;}
Se nenhuma dessas abordagens funcionar, então é possível criar um rastreamento de pilha aproximado sem números de linha ou colunas, iterando sobre o objeto arguments.callee.caller
- isso não funcionará no modo estrito ES5 e não é uma abordagem recomendada.
É muito comum que pontos assíncronos sejam inseridos no código JavaScript, como quando o código usa setTimeout
ou através do uso de Promises. Esses pontos de entrada assíncronos podem causar problemas para rastreamentos de pilha, pois causam a formação de um novo contexto de execução e o rastreamento de pilha começa do zero novamente.
O Chrome DevTools tem suporte para rastreamentos de pilha assíncronos ou, em outras palavras, garante que o rastreamento de pilha de um erro também mostre os frames que ocorreram antes da introdução do ponto assíncrono. Com o uso de setTimeout, isso irá capturar quem chamou a função setTimeout que eventualmente produziu um erro. Consulte http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/ para obter mais informações.
Um rastreamento de pilha assíncrono será semelhante a:
throwError @ throw-error.js:2 setTimeout (async) throwErrorAsync @ throw-error.js:10 (anonymous function) @ throw-error-basic.html:14
Os rastreamentos de pilha assíncronos só são suportados no Chrome DevTools no momento, apenas para exceções lançadas quando o DevTools está aberto. Os rastreamentos de pilha acessados a partir de objetos Error no código não terão o rastreamento de pilha assíncrono como parte dele.
É possível fazer polyfill de rastreamentos de pilha assíncronos em alguns casos, mas isso pode causar um impacto significativo no desempenho do seu aplicativo, pois capturar um rastreamento de pilha não é barato.
Somente o Chrome DevTools oferece suporte nativo a rastreamentos de pilha assíncronos.
Os rastreamentos de pilha para código que foi avaliado ou incorporado em uma página HTML usarão o URL da página e os números de linha/coluna para o código executado.
Por exemplo:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
Se esses scripts realmente vierem de um script que foi incorporado por motivos de otimização, o URL, a linha e os números das colunas estarão errados. Para contornar esse problema, o Chrome e o Firefox suportam a anotação //# sourceURL=
(Safari, Edge e IE não). O URL especificado nesta anotação será usado como URL para todos os rastreamentos de pilha, e o número da linha e da coluna será calculado em relação ao início da tag em vez do documento HTML. Para o mesmo erro acima, usar a anotação sourceURL com um valor "inline.js" produzirá um rastreamento de pilha semelhante a:
at throwError (http://mknichel.github.io/javascript-errors/inline.js:8:9) at http://mknichel.github.io/javascript-errors/inline.js:12:3
Esta é uma técnica realmente útil para garantir que os rastreamentos de pilha ainda estejam corretos, mesmo ao usar scripts embutidos e avaliação.
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl descreve a anotação sourceURL com mais detalhes.
Safari, Edge e IE não suportam a anotação sourceURL para nomear scripts e avaliações in-line. Se você usar scripts embutidos no IE ou Safari e ofuscar seu código, não será possível desofuscar erros provenientes desses scripts.
Até o Chrome 42, o Chrome não calculava corretamente os números de linha para scripts embutidos que usam a anotação sourceURL. Consulte https://bugs.chromium.org/p/v8/issues/detail?id=3920 para obter mais informações.
Os números de linha para quadros de pilha de scripts embutidos estão incorretos quando a anotação sourceURL é usada, pois são relativos ao início do documento HTML em vez do início da tag de script embutido (impossibilitando a desofuscação correta). https://code.google.com/p/chromium/issues/detail?id=578269
Para código que usa eval, há outras diferenças no rastreamento de pilha além de usar ou não a anotação sourceURL. No Chrome, um rastreamento de pilha de uma instrução usada em eval poderia ser assim:
Error: Error from eval at evaledFunction (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3),:1:36) at eval (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3), :1:68) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
No MS Edge e no IE11, seria assim:
Error from eval at evaledFunction (eval code:1:30) at eval code (eval code:1:2) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
No Safári:
Error from eval evaledFunction eval code eval@[native code] evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:7
e no Firefox:
Error from eval evaledFunction@http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:36 @http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:11 evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3
Essas diferenças podem dificultar a análise do código de avaliação da mesma forma em todos os navegadores.
Cada navegador usa um formato de rastreamento de pilha diferente para erros que ocorreram dentro do eval.
Seu código JavaScript também pode ser chamado diretamente do código nativo. Array.prototype.forEach
é um bom exemplo - você passa uma função para forEach
e o mecanismo JS chamará essa função para você.
function throwErrorWithNativeFrame() { var arr = [0, 1, 2, 3]; arr.forEach(função nomeadaFn(valor) {throwError(); });}
Isso produz diferentes rastreamentos de pilha em diferentes navegadores. O Chrome e o Safari acrescentam o nome da função nativa no rastreamento de pilha como um quadro separado, como:
(Chrome) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.forEach (native) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7) (Safari) namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:15 forEach@[native code] throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:14 (Edge) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.prototype.forEach (native code) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7)
No entanto, o Firefox e o IE11 não mostram que forEach
foi chamado como parte da pilha:
namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5 throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:3
Alguns navegadores incluem frames de código nativos em rastreamentos de pilha, enquanto outros não.
Para detectar que seu aplicativo apresentou um erro, algum código deve ser capaz de detectar esse erro e reportá-lo. Existem várias técnicas para detectar erros, cada uma com seus prós e contras.
window.onerror
é uma das melhores e mais fáceis maneiras de começar a detectar erros. Ao atribuir window.onerror
a uma função, qualquer erro que não seja detectado por outra parte do aplicativo será relatado a esta função, juntamente com algumas informações sobre o erro. Por exemplo:
window.onerror = function(msg, url, linha, col, err) { console.log('O aplicativo encontrou um erro: ' + msg); console.log('Rastreamento de pilha: ' + err.stack);}
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror descreve isso com mais detalhes.
Historicamente, houve alguns problemas com esta abordagem:
Nenhum objeto de erro fornecido
O quinto argumento para a função window.onerror
deve ser um objeto Error. Isso foi adicionado à especificação WHATWG em 2013: https://html.spec.whatwg.org/multipage/webappapis.html#errorevent. Chrome, Firefox e IE11 agora fornecem corretamente um objeto Error (junto com a propriedade de pilha crítica), mas Safari, MS Edge e IE10 não. Isso funciona no Firefox desde o Firefox 14 (https://bugzilla.mozilla.org/show_bug.cgi?id=355430) e no Chrome desde o final de 2013 (https://mikewest.org/2013/08/debugging-runtime-errors -com-janela-onerror, https://code.google.com/p/chromium/issues/detail?id=147127). O Safari 10 lançou suporte para o objeto Error em window.onerror.
Safari (versões abaixo de 10), MS Edge e IE10 não suportam um objeto Error com rastreamento de pilha em window.onerror.
Sanitização entre domínios
No Chrome, os erros provenientes de outro domínio no manipulador window.onerror serão limpos para "Erro de script.", "", 0. Geralmente, isso é aceitável se você realmente não deseja processar o erro se ele vier de um script com o qual você não se importa, para que o aplicativo possa filtrar erros semelhantes a este. No entanto, isso não acontece no Firefox, no Safari ou no IE11, nem o Chrome faz isso para blocos try/catch que agrupam o código incorreto.
Se você quiser receber erros em window.onerror
no Chrome com total fidelidade de scripts de domínio cruzado, esses recursos deverão fornecer os cabeçalhos de origem cruzada apropriados. Consulte https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror para obter mais informações.
O Chrome é o único navegador que irá limpar erros provenientes de outra origem. Tenha cuidado para filtrá-los ou defina os cabeçalhos apropriados.
Extensões do Chrome
Nas versões antigas do Chrome, as extensões do Chrome instaladas na máquina de um usuário também podem gerar erros que são relatados para window.onerror. Isso foi corrigido nas versões mais recentes do Chrome. Consulte a seção dedicada às extensões do Chrome abaixo.
A API window.addEventListener("error")
funciona da mesma forma que a API window.onerror. Consulte http://www.w3.org/html/wg/drafts/html/master/webappapis.html#runtime-script-errors para obter mais informações sobre esta abordagem.
A captura de erros por meio de window.onerror não impede que esse erro também apareça no console do DevTools. Este é provavelmente o comportamento correto para o desenvolvimento, pois o desenvolvedor pode ver facilmente o erro. Se você não deseja que esses erros apareçam na produção para os usuários finais, e.preventDefault()
pode ser chamado se estiver usando a abordagem window.addEventListener.
window.onerror é a melhor ferramenta para detectar e relatar erros de JS. É recomendado que apenas erros JS com objetos Error e rastreamentos de pilha válidos sejam relatados ao servidor. Caso contrário, os erros poderão ser difíceis de investigar ou você poderá receber muito spam de extensões do Chrome ou scripts de vários domínios.
Dada a seção acima, infelizmente não é possível confiar em window.onerror
em todos os navegadores para capturar todas as informações de erro. Para capturar exceções localmente, um bloco try/catch é a escolha óbvia. Também é possível agrupar arquivos JavaScript inteiros em um bloco try/catch para capturar informações de erro que não podem ser detectadas com window.onerror. Isso melhora a situação dos navegadores que não suportam window.onerror, mas também tem algumas desvantagens.
Um bloco try/catch não capturará todos os erros em um programa, como erros lançados de um bloco de código assíncrono por meio de window.setTimeout
. Try/catch pode ser usado com pontos de entrada protegidos para ajudar a preencher as lacunas.
Os blocos try/catch que envolvem todo o aplicativo não são suficientes para detectar todos os erros.
Versões antigas do V8 (e potencialmente de outros mecanismos JS), funções que contêm um bloco try/catch não serão otimizadas pelo compilador (http://www.html5rocks.com/en/tutorials/speed/v8/). O Chrome corrigiu isso no TurboFan (https://codereview.chromium.org/1996373002).
Um “ponto de entrada” no JavaScript é qualquer API do navegador que possa iniciar a execução do seu código. Os exemplos incluem setTimeout
, setInterval
, ouvintes de eventos, XHR, web sockets ou promessas. Erros lançados a partir desses pontos de entrada serão capturados por window.onerror, mas nos navegadores que não suportam o objeto Error completo em window.onerror, um mecanismo alternativo é necessário para capturar esses erros, já que o método try/catch mencionado acima também não os pegará.
Felizmente, o JavaScript permite que esses pontos de entrada sejam agrupados para que um bloco try/catch possa ser inserido antes que a função seja invocada para capturar quaisquer erros gerados pelo código.
Cada ponto de entrada precisará de um código ligeiramente diferente para protegê-lo, mas a essência da metodologia é:
função protegerEntryPoint(fn) { return function protectedFn() {try { return fn();} catch (e) { // Tratar erro.} }}_oldSetTimeout = window.setTimeout;window.setTimeout = função protectedSetTimeout(fn, tempo) { return _oldSetTimeout.call(janela, protejaEntryPoint(fn), hora);};
Infelizmente, é fácil que erros que acontecem no Promises passem despercebidos e não sejam relatados. Erros que acontecem em um Promise, mas não são tratados pela anexação de um manipulador de rejeição, não são relatados em nenhum outro lugar - eles não são relatados para window.onerror
. Mesmo que uma promessa anexe um manipulador de rejeição, esse próprio código deve relatar manualmente esses erros para que sejam registrados. Consulte http://www.html5rocks.com/en/tutorials/es6/promises/#toc-error-handling para obter mais informações. Por exemplo:
janela.onerror = função(...) { // Isso nunca será invocado pelo código Promise.};var p = new Promise(...);p.then(function() { throw new Error("Este erro não será tratado em lugar nenhum.");});var p2 = new Promise(...);p2.then(function() { throw new Error("Este erro será tratado na cadeia.");}).catch(function(error) { //Mostra mensagem de erro para o usuário // Este código deverá reportar manualmente o erro para que ele seja logado no servidor, se for o caso.});
Uma abordagem para capturar mais informações é usar pontos de entrada protegidos para agrupar invocações de métodos Promise com um try/catch para relatar erros. Isso pode ser parecido com:
var _oldPromiseThen = Promise.prototype.then; Promise.prototype.then = função protectedThen(callback, errorHandler) {return _oldPromiseThen.call(this, ProtectEntryPoint(callback), ProtectEntryPoint(errorHandler)); };
Infelizmente, os erros do Promises não serão tratados por padrão.
Implementações de Promise, como Q, Bluebird e Closure, tratam erros de maneiras diferentes que são melhores do que o tratamento de erros na implementação de Promises no navegador.
Em Q, você pode "encerrar" a cadeia Promise chamando .done()
que garantirá que, se um erro não for tratado na cadeia, ele será relançado e relatado. Consulte https://github.com/kriskowal/q#handling-errors
No Bluebird, as rejeições não tratadas são registradas e relatadas imediatamente. Consulte http://bluebirdjs.com/docs/features.html#surfacing-unhandled-errors
Na implementação goog.Promise do Closure, as rejeições não tratadas são registradas e relatadas se nenhuma cadeia no Promise tratar a rejeição dentro de um intervalo de tempo configurável (para permitir que o código posteriormente no programa adicione um manipulador de rejeição).
A seção de rastreamento de pilha assíncrona acima discute que os navegadores não capturam informações da pilha quando há um gancho assíncrono, como chamar Promise.prototype.then
. Os polyfills Promise apresentam uma maneira de capturar os pontos de rastreamento da pilha assíncrona, o que pode tornar o diagnóstico de erros muito mais fácil. Essa abordagem é cara, mas pode ser muito útil para capturar mais informações de depuração.
Em Q, chame Q.longStackSupport = true;
. Consulte https://github.com/kriskowal/q#long-stack-traces
No Bluebird, chame Promise.longStackTraces()
em algum lugar do aplicativo. Consulte http://bluebirdjs.com/docs/features.html#long-stack-traces.
Em Closure, defina goog.Promise.LONG_STACK_TRACES
como true.
O Chrome 49 adicionou suporte para eventos que são enviados quando uma promessa é rejeitada. Isso permite que os aplicativos se conectem aos erros do Promise para garantir que eles sejam relatados centralmente junto com o restante dos erros.
window.addEventListener('rejeição não tratada', evento => { // event.reason contém o motivo da rejeição. Quando um Error é lançado, este é o objeto Error.});
Consulte https://googlechrome.github.io/samples/promise-rejection-events/ e https://www.chromestatus.com/feature/4805872211460096 para obter mais informações.
Isso não é compatível com nenhum outro navegador.
Os trabalhadores da Web, incluindo trabalhadores dedicados, trabalhadores compartilhados e trabalhadores de serviços, estão se tornando mais populares nos aplicativos atualmente. Como todos esses trabalhadores são scripts separados da página principal, cada um deles precisa de seu próprio código de tratamento de erros. Recomenda-se que cada script de trabalho instale seu próprio código de tratamento de erros e de relatório para obter a máxima eficácia no tratamento de erros de trabalhadores.
Web workers dedicados são executados em um contexto de execução diferente da página principal, portanto, os erros dos trabalhadores não são detectados pelos mecanismos acima. Etapas adicionais precisam ser executadas para capturar erros de trabalhadores na página.
Quando um trabalhador é criado, a propriedade onerror pode ser definida no novo trabalhador:
var trabalhador = new Trabalhador('worker.js');worker.onerror = function(errorEvent) { ... };
Isso é definido em https://html.spec.whatwg.org/multipage/workers.html#handler-abstractworker-onerror. A função onerror
no trabalhador tem uma assinatura diferente da window.onerror
discutida acima. Em vez de aceitar 5 argumentos, worker.onerror
aceita um único argumento: um objeto ErrorEvent
. A API para este objeto pode ser encontrada em https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent. Ele contém a mensagem, o nome do arquivo, a linha e a coluna, mas nenhum navegador estável hoje contém o objeto "Error" que contém o rastreamento de pilha (errorEvent.error é nulo). Como esta API é executada no escopo da página pai, seria útil usar o mesmo mecanismo de relatório da página pai; infelizmente, devido à falta de rastreamento de pilha, esta API é de uso limitado.
Dentro do JS executado pelo trabalhador, você também pode definir uma API onerror que segue a API window.onerror usual: https://html.spec.whatwg.org/multipage/webappapis.html#onerroreventhandler. No código do trabalhador:
self.onerror = function(mensagem, nome do arquivo, linha, coluna, erro) { ... };
A discussão desta API segue principalmente a discussão acima para window.onerror. No entanto, há duas coisas notáveis a serem destacadas:
O Firefox e o Safari não relatam o objeto "erro" como o quinto argumento da função, portanto, esses navegadores não obtêm um rastreamento de pilha do trabalhador (Chrome, MS Edge e IE11 obtêm um rastreamento de pilha). Pontos de entrada protegidos para a função onmessage
dentro do trabalhador podem ser usados para capturar informações de rastreamento de pilha para esses navegadores.
Como esse código é executado no trabalhador, o código deve escolher como relatar o erro ao servidor: ele deve usar postMessage
para comunicar o erro à página pai ou instalar um mecanismo de relatório de erros XHR (discutido mais abaixo) em o próprio trabalhador.
No Firefox, Safari e IE11 (mas não no Chrome), a função window.onerror
da página pai também será chamada depois que o onerror do próprio trabalhador e o ouvinte de evento onerror definido pela página forem chamados. No entanto, este window.onerror também não conterá um objeto de erro e, portanto, também não terá um rastreamento de pilha. Esses navegadores também devem tomar cuidado para não reportar erros de trabalhadores diversas vezes.
O Chrome e o Firefox oferecem suporte à API SharedWorker para compartilhar um trabalhador entre várias páginas. Como o trabalhador é compartilhado, ele não é anexado exclusivamente a uma página pai; isso leva a algumas diferenças na forma como os erros são tratados, embora o SharedWorker siga principalmente as mesmas informações que o web trabalhador dedicado.
No Chrome, quando há um erro em um SharedWorker, apenas o tratamento de erros do próprio trabalhador dentro do próprio código do trabalhador será chamado (como se eles definissem self.onerror
). O window.onerror
da página pai não será chamado e o Chrome não oferece suporte ao AbstractWorker.onerror
herdado que pode ser chamado na página pai conforme definido na especificação.
No Firefox, esse comportamento é diferente. Um erro no trabalhador compartilhado fará com que o window.onerror da página pai seja chamado, mas o objeto de erro será nulo. Além disso, o Firefox suporta a propriedade AbstractWorker.onerror
, para que a página pai possa anexar um manipulador de erros próprio ao trabalhador. No entanto, quando esse manipulador de erros for chamado, o objeto de erro será nulo, portanto não haverá rastreamento de pilha, portanto, é de uso limitado.
O tratamento de erros para trabalhadores compartilhados difere de acordo com o navegador.
Service Workers são uma especificação totalmente nova que atualmente está disponível apenas nas versões recentes do Chrome e Firefox. Esses trabalhadores seguem a mesma discussão que os trabalhadores da web dedicados.
Os service workers são instalados chamando a função navigator.serviceWorker.register
. Esta função retorna uma promessa que será rejeitada se houver um erro ao instalar o service worker, como gerar um erro durante a inicialização. Este erro conterá apenas uma mensagem de string e nada mais. Além disso, como o Promises não relata erros aos manipuladores window.onerror
, o próprio aplicativo teria que adicionar um bloco catch ao Promise para detectar o erro.
navigator.serviceWorker.register('service-worker-installation-error.js').catch(function(error) { // erro tipo de string});
Assim como os outros trabalhadores, os prestadores de serviço podem definir uma função self.onerror
dentro dos prestadores de serviço para detectar erros. Erros de instalação no service worker serão relatados à função onerror, mas infelizmente não conterão um objeto de erro ou rastreamento de pilha.
A API do service worker contém uma propriedade onerror herdada da interface AbstractWorker, mas o Chrome não faz nada