Atualizado: 05/07/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Tratamento unificado de exceções em JavaScript

Em aplicações JavaScript modernas, especialmente em projetos maiores ou com múltiplas camadas (como back-end e front-end), é comum que o tratamento de exceções se torne repetitivo e fragmentado. Blocos try...catch aninhados dificultam a leitura, aumentam o acoplamento e dificultam a manutenção.

Uma abordagem mais limpa e previsível consiste em adotar um padrão unificado para lidar com erros, encapsulando chamadas em funções auxiliares que retornam resultados estruturados. Esse padrão simplifica o fluxo de controle, melhora a depuração e favorece a escrita de código mais funcional e reutilizável.


Encapsulando exceções com tryCatch

A função tryCatch executa qualquer função passada como argumento e captura automaticamente erros, retornando um array no formato [resultado, erro]:

function tryCatch(fn) {
  try {
    return [fn(), null];
  } catch (err) {
    return [null, err];
  }
}

Se a execução for bem-sucedida, o erro será null. Caso contrário, o resultado será null e o erro capturado poderá ser tratado externamente, sem a necessidade de blocos try...catch explícitos em cada chamada.

Exemplo: cálculo de raiz quadrada

A função sqrt abaixo calcula a raiz quadrada de um número, mas lança um erro se o valor for negativo:

const sqrt = (x) => {
  if (x < 0) throw new Error(`Number ${x} is invalid`);
  return Math.sqrt(x);
};

Com o wrapper:

const [res1, err1] = tryCatch(() => sqrt(-4));
if (err1) console.error(err1);
else console.log("sqrt(-4):", res1);

const [res2, err2] = tryCatch(() => sqrt(4));
if (err2) console.error(err2);
else console.log("sqrt(4):", res2);

No primeiro caso, sqrt(-4) gera uma exceção que é capturada. No segundo, o resultado é 2, sem erro.


Aplicando a construtores

A mesma estratégia pode ser aplicada ao instanciamento de classes. Veja um exemplo com uma classe Person:

class Person {
  constructor(name, age) {
    if (age < 0) throw `Idade inválida ${age}. Valor mínimo: 1`;
    if (name.length < 2) throw `Nome inválido ${name}: mínimo de 2 caracteres`;
    this.name = name;
    this.age = age;
  }

  print() {
    console.log(`Name: ${this.name}  Age: ${this.age}`);
  }
}

Instanciando com o wrapper:

const [tom, err1] = tryCatch(() => new Person("Tom", -123));
if (err1) console.error(err1);
else tom.print();

const [bob, err2] = tryCatch(() => new Person("Bob", 46));
if (err2) console.error(err2);
else bob.print();

O primeiro construtor lança erro ao receber idade negativa, e o segundo instancia normalmente.


Garantindo consistência no tipo de erro

Como JavaScript permite lançar qualquer tipo de valor como erro (não apenas instâncias de Error), é possível adaptar tryCatch para converter qualquer valor em um erro padrão:

function tryCatch(fn) {
  try {
    return [fn(), null];
  } catch (err) {
    const error = err instanceof Error ? err : new Error(String(err));
    return [null, error];
  }
}

Isso padroniza os erros, facilitando o tratamento e o monitoramento.


Versão reutilizável com argumentos

Uma variação útil da função tryCatch é um wrapper reutilizável, que retorna uma nova função com tratamento interno de exceções. Essa abordagem permite reaproveitar a lógica de captura de erros em múltiplas chamadas, sem precisar embrulhar cada execução manualmente.

const tryWrap =
  (fn) =>
  (...args) => {
    try {
      return [fn(...args), null];
    } catch (err) {
      return [null, err];
    }
  };

Esse wrapper recebe a função original como argumento e devolve outra função que aceita quaisquer parâmetros e executa a chamada original com tratamento de erro. O retorno segue o mesmo padrão: [resultado, erro].

tryWrap devolve a função já protegida, pronta para ser chamada diretamente:

const safeSqrt = tryWrap(sqrt);

const [res1, err1] = safeSqrt(-4);
const [res2, err2] = safeSqrt(16);

Quando a função recebe vários argumentos, o ganho de clareza fica mais evidente:

const safePerson = tryWrap((name, age) => new Person(name, age));

const [tom, tomErr] = safePerson("Tom", -123);
const [bob, bobErr] = safePerson("Bob", 46);

A função original continua inalterada, mas agora existe uma forma segura e reaproveitável de chamá-la.


Retorno estruturado nativamente

Outra opção é fazer com que a própria função já siga o contrato de retorno [resultado, erro], sem depender de wrappers externos:

const sqrt = (x) => {
  if (x < 0) return [null, new Error(`Number ${x} is invalid`)];
  return [Math.sqrt(x), null];
};

Com esse padrão, o código se torna ainda mais direto:

const [res1, err1] = sqrt(-4);
if (err1) console.error(err1);
else console.log("sqrt(-4):", res1);

const [res2, err2] = sqrt(4);
if (err2) console.error(err2);
else console.log("sqrt(4):", res2);

Conclusão

O uso de wrappers ou contratos de retorno padronizados oferece uma forma mais limpa, segura e previsível de lidar com exceções em JavaScript. Essa abordagem reduz a necessidade de blocos try...catch dispersos, melhora a legibilidade, facilita testes e torna o código mais compatível com estilos funcionais e ambientes complexos.

O mesmo padrão pode ser adaptado para funções assíncronas, com versões compatíveis com async/await, mantendo a consistência no tratamento de exceções em toda a base do projeto.

Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com