O tratamento de erros é uma parte essencial da programação, pois permite que seu código lide com situações inesperadas de forma controlada. Em JavaScript, as instruções try, catch e finally fornecem uma estrutura poderosa para capturar e gerenciar exceções. Nesta aula, exploraremos cada um desses componentes, além de como lançar erros intencionalmente com throw, entender o objeto Error e seus subtipos, e seguir boas práticas para escrever código mais seguro e legível.

throw

A instrução throw permite que você lance uma exceção personalizada. Quando uma exceção é lançada, a execução normal do código é interrompida e o controle é transferido para o bloco catch mais próximo. Você pode lançar qualquer valor, mas é recomendável lançar um objeto Error (ou uma de suas subclasses) para manter a consistência e facilitar a depuração.

Exemplo simples:

function dividir(a, b) {
  if (b === 0) {
    throw new Error('Divisão por zero não é permitida');
  }
  return a / b;
}

try {
  console.log(dividir(10, 0));
} catch (erro) {
  console.error(erro.message); // 'Divisão por zero não é permitida'
}

No exemplo, a função dividir lança um erro se o divisor for zero. O bloco try chama a função com um valor inválido, e o catch captura o erro e exibe sua mensagem.

Objeto Error

O objeto Error é a base para todas as exceções em JavaScript. Ele possui propriedades padrão como name (tipo do erro) e message (descrição). Além disso, a propriedade stack contém o rastreamento da pilha de chamadas, útil para depuração.

Exemplo de criação e uso:

const erro = new Error('Algo deu errado');
console.log(erro.name);    // 'Error'
console.log(erro.message); // 'Algo deu errado'
console.log(erro.stack);   // Stack trace (depende do ambiente)

Você também pode criar subclasses de Error para representar tipos específicos de erro, como TypeError, RangeError, etc. Essas subclasses herdam de Error e podem ter propriedades adicionais.

Tipos de erro

JavaScript fornece vários tipos de erro internos que correspondem a situações comuns. Os principais são:

  • SyntaxError: Ocorre quando há um erro de sintaxe no código (ex.: parênteses desbalanceados).
  • ReferenceError: Lançado quando uma variável não é encontrada no escopo.
  • TypeError: Ocorre quando uma operação é realizada em um tipo inadequado (ex.: chamar algo que não é função).
  • RangeError: Quando um valor numérico está fora do intervalo permitido (ex.: array com tamanho negativo).
  • URIError: Relacionado a funções de URI como decodeURI().
  • EvalError: Erro na função eval() (raro).

Exemplo que gera um TypeError:

try {
  null.f();
} catch (erro) {
  console.log(erro instanceof TypeError); // true
  console.log(erro.message);              // 'Cannot read property 'f' of null'
}

Boas práticas

Ao trabalhar com tratamento de erros, siga estas recomendações para manter o código limpo e eficiente:

  • Use tipos específicos de erro: Lance TypeError, RangeError, etc., quando apropriado, em vez de um Error genérico.
  • Não abuse de try/catch: Use tratamento de erros apenas para situações excepcionais, não para controle de fluxo normal.
  • Sempre trate erros em operações assíncronas: Em Promises, use .catch() ou try/catch com async/await.
  • Registre erros adequadamente: Em produção, registre erros em um serviço de log para monitoramento.
  • Não engula erros: Evite blocos catch vazios ou que apenas registram e continuam sem ação.
  • Use finally para limpeza: Coloque código de liberação de recursos (fechar arquivos, conexões) no bloco finally, que sempre executa.

Exemplo de boas práticas com finally:

let recurso;
try {
  recurso = abrirRecurso();
  // operações com o recurso
} catch (erro) {
  console.error('Erro ao manipular recurso:', erro);
} finally {
  if (recurso) {
    recurso.fechar();
  }
}

Referências

Exercícios

  1. Crie uma função que recebe dois números e retorna a divisão. Se o divisor for zero, lance um erro com a mensagem "Divisão por zero". Use try/catch para capturar e exibir a mensagem.

    ✓ Resposta:
    function dividir(a, b) {
      if (b === 0) {
        throw new Error('Divisão por zero');
      }
      return a / b;
    }
    
    try {
      console.log(dividir(10, 0));
    } catch (erro) {
      console.error(erro.message);
    }
  2. Escreva um código que tenta acessar uma propriedade de undefined e capture o erro. Verifique se o erro é uma instância de TypeError.

    ✓ Resposta:
    try {
      undefined.x;
    } catch (erro) {
      console.log(erro instanceof TypeError); // true
      console.log(erro.message);
    }
  3. Crie uma função que valida uma idade. Se a idade for menor que 0 ou maior que 150, lance um RangeError com a mensagem "Idade inválida". Capture o erro e exiba o nome e a mensagem.

    ✓ Resposta:
    function validarIdade(idade) {
      if (idade < 0 || idade > 150) {
        throw new RangeError('Idade inválida');
      }
      return idade;
    }
    
    try {
      validarIdade(200);
    } catch (erro) {
      console.log(erro.name);    // 'RangeError'
      console.log(erro.message); // 'Idade inválida'
    }
  4. Use um bloco finally para garantir que uma mensagem "Fim da operação" seja exibida independentemente de ocorrer erro ou não. Simule um erro dentro do try.

    ✓ Resposta:
    try {
      throw new Error('Erro simulado');
    } catch (erro) {
      console.error(erro.message);
    } finally {
      console.log('Fim da operação');
    }
  5. Escreva uma função que tenta analisar um JSON (JSON.parse) e capture erros de sintaxe (SyntaxError). Exiba uma mensagem amigável para o usuário.

    ✓ Resposta:
    function analisarJSON(texto) {
      try {
        return JSON.parse(texto);
      } catch (erro) {
        if (erro instanceof SyntaxError) {
          console.error('JSON inválido. Verifique a sintaxe.');
        } else {
          throw erro;
        }
      }
    }
    
    analisarJSON('{invalido}');