Javascript, construído em Error
fornece informações úteis, mas muitas vezes pode sentir falta de clareza. One-size-fits-all é ótimo para o idioma, mas podemos fazer melhor em nossos próprios aplicativos. É aí que entram erros personalizados.
você pode ter visto erros personalizados ao usar o Node.js. O nó é construído em tipos de erro como AssertionError
, RangeError
, ReferenceError
, SyntaxError
, e SystemError
são todas extensões da classe nativa Error
.
usar Error
como base oferece todo o poder da implementação do Javascript, além de benefícios adicionais. Aqui estão apenas alguns:
- formatos de mensagem de erro personalizados.
- propriedades adicionais no objeto de erro.
- erros nomeados para facilitar a depuração e o tratamento condicional de erros.
- erros consistentes para os consumidores da biblioteca referirem.
estendendo a classe de erro
para começar, vamos usar um exemplo genérico. Estenderemos a classe Error
e usaremos super
para herdar suas funções.
class CustomError extends Error { constructor(...params) { super(...params) // We're spreading `params` as a way to bring all of `Error`'s functionality in. }}
agora, quando você throw
um erro, você pode fazê-lo com throw new CustomError("Something went wrong...")
. Também oferece a capacidade de verificar erros em relação ao tipo:
try { throw new CustomError("Something went wrong")} catch (error) { if (error instance of CustomError) { // do something specifically for that type of error }}
isso por si só não faz muito, além de fornecer um novo tipo de erro para ligar e verificar. Para entender melhor o que pode ser feito, vamos ver o que vem padrão em Error
.
- Error.name
- Erro.mensagem
- erro.prototipo.toString ()
- erro.prototipo.construtor()
não há muito com que trabalhar? Além do construtor e do método toString
, o nome do erro e a mensagem que descreve o erro são as únicas partes que provavelmente usaremos. No exemplo acima, message
é definido passando “algo deu errado” como o argumento ao instanciar o erro.
felizmente, a maioria das plataformas javascript como o navegador e o nó.js adicionaram seus próprios métodos e propriedades em cima dos listados. Vamos nos concentrar em alguns aspectos da implementação de erros da V8, o mecanismo Javascript que alimenta o Chrome e o Node.js.
as duas áreas de interesse são stack
e captureStackTrace
. Como seus nomes sugerem, eles permitem que você afunde o rastreamento da pilha. Vamos ver como isso se parece com um exemplo.
class CustomError extends Error { constructor(...args) { super(...args) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.name = "Our Custom Error" }}try { throw new CustomError("Something went wrong")} catch (err) { console.error(err.stack)}
neste exemplo, estamos chamando Error.captureStackTrace
, se a plataforma suportar, para garantir que um rastreamento completo seja adicionado ao nosso erro personalizado. Em seguida, throw
o erro de dentro do bloco try
, que passará o controle para o bloco catch
.
se você executar o código acima, verá que a primeira linha do stack
é o erro name
e message
.
Our Custom Error: Something went wrong
este erro não é muito” profundo”, então a pilha é principalmente nó.js internos. Cada linha é um “quadro” da pilha. Eles contêm detalhes sobre a localização do erro na base de código.Agora que sabemos como fazer e chamar um erro personalizado, vamos torná-lo útil.
personalizar o conteúdo do erro
o erro personalizado com o qual trabalhamos é bom, mas vamos alterá-lo para torná-lo mais útil. A classe será CircuitError
. Vamos usá-lo para fornecer erros personalizados provenientes de um disjuntor. Se você não está familiarizado com o padrão, tudo bem. Os detalhes de implementação do disjuntor não são importantes. O importante é que possamos passar algumas informações para o erro, e ele apresentará essas informações ao usuário.
um disjuntor tem um estado “aberto” que não permite que nada passe por ele por um período fixo de tempo. Vamos definir nosso CircuitError
para conter alguns detalhes que podem ser úteis para as funções que o recebem quando o estado é OPEN
.
class CircuitError extends Error { // 1 constructor(state, nextAttempt, ...params) { super(...params) if (Error.captureStackTrace) { Error.captureStackTrace(this, CircuitError) } this.name = "CircuitError" this.state = state // 2 this.message = `The Circuit is ${state}.` // 3 if (nextAttempt) { this.timestamp = Date.now() this.nextAttempt = nextAttempt this.message += ` Next attempt can be made in ${this.nextAttempt - this.timestamp}ms.` } }}
nosso erro atualizado faz algumas coisas novas, tudo dentro do construtor. Os argumentos passados mudaram (1) para incluir o state
do disjuntor, bem como um carimbo de data / hora nextAttempt
para indicar quando o disjuntor começará a tentar solicitações novamente.
em seguida, Ele define algumas novas propriedades e atualiza a mensagem, dependendo dos valores apresentados. Podemos testá-lo lançando uma nova versão desse erro.
try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { console.error(err)}
agora, quando lançamos o erro, ele toma o estado como o primeiro argumento e um carimbo de data / hora como o segundo. Para esta demonstração, estamos passando no tempo 8000 milissegundos no futuro. Observe que também estamos registrando o erro em si, em vez de apenas a pilha.
executar o código resultará em algo como o seguinte:
CircuitError : The Circuit is OPEN. Next attempt can be made in 6000ms. at Object.<anonymous> (/example.js:21:9) at Module._compile (internal/modules/cjs/loader.js:1063:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1103:10) at Module.load (internal/modules/cjs/loader.js:914:32) at Function.Module._load (internal/modules/cjs/loader.js:822:14) at Function.Module.runMain (internal/modules/cjs/loader.js:1143:12) at internal/main/run_main_module.js:16:11 { name: 'Circuit Error', state: 'OPEN', message: 'The Circuit is OPEN. Next attempt can be made in 6000ms.', timestamp: 1580242308919, nextAttempt: 1580242314919}
o resultado é o nome do erro seguido pela mensagem na primeira linha. Em seguida, temos o resto da pilha, seguido por todas as propriedades que definimos no CircuitError
. Nossos consumidores desse erro poderiam usar esses dados para reagir ao erro. Por exemplo:
try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { customLogger(err) if (err.state === "OPEN") { handleOpenState(err.nextAttempt) }}
em vez de um erro genérico, agora temos algo mais adequado às necessidades de nosso aplicativo.
Reagir a falhas
erros Personalizados são uma ótima maneira de reagir a falhas. É comum supor que todos os erros são inesperados, mas ao fornecer erros úteis, você pode tornar sua existência mais fácil de lidar. Ao fazer uso de erros personalizados, não apenas podemos fornecer aos usuários de nossos aplicativos dados valiosos, mas também apresentamos a capacidade de responder a tipos de erros por meio da condição instance of
.
no Bearer, usamos erros personalizados para padronizar as respostas em nosso código, fornecer apenas as informações de que você precisa em nosso cliente e tornar nossos erros acionáveis.
como você está usando erros personalizados em seus aplicativos? Conecte-se conosco @BearerSH e deixe-nos saber.