Tratamento de Erros e Debugging
Aula 7 de 8
Tratamento de Erros
try/catch/finally
try {
const resultado = operacaoRiscada();
console.log('Sucesso:', resultado);
} catch (erro) {
console.error('Erro capturado:', erro.message);
console.error('Stack:', erro.stack);
} finally {
console.log('Sempre executa, com ou sem erro');
}
Error e Throw
// Lançar erro
function dividir(a, b) {
if (b === 0) {
throw new Error('Divisão por zero não permitida');
}
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Ambos os parâmetros devem ser números');
}
return a / b;
}
try {
console.log(dividir(10, 0));
} catch (erro) {
if (erro instanceof TypeError) {
console.error('Erro de tipo:', erro.message);
} else {
console.error('Erro:', erro.message);
}
}
Custom Error Classes
class ValidationError extends Error {
constructor(campo, mensagem) {
super(mensagem);
this.name = 'ValidationError';
this.campo = campo;
this.timestamp = new Date().toISOString();
}
}
class NotFoundError extends Error {
constructor(recurso, id) {
super(`${recurso} com id ${id} não encontrado`);
this.name = 'NotFoundError';
this.statusCode = 404;
}
}
function buscarUsuario(id) {
if (typeof id !== 'number') {
throw new ValidationError('id', 'ID deve ser um número');
}
const usuario = banco.find(u => u.id === id);
if (!usuario) {
throw new NotFoundError('Usuário', id);
}
return usuario;
}
Tratamento de Erros Assíncronos
// Async/await com try/catch
async function carregarDados() {
try {
const response = await fetch('https://api.exemplo.com/dados');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (erro) {
console.error('Falha ao carregar dados:', erro);
throw erro; // repassar
}
}
// Caso de uso real: retry com fallback
async function fetchComRetry(url, tentativas = 3) {
for (let i = 0; i < tentativas; i++) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`Status ${response.status}`);
return await response.json();
} catch (erro) {
if (i === tentativas - 1) throw erro;
console.warn(`Tentativa ${i + 1} falhou. Tentando novamente...`);
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}
Debugging
Console API
// Básico
console.log('Mensagem simples');
console.error('Erro vermelho');
console.warn('Aviso amarelo');
console.info('Informação');
// Estruturado
console.table([
{ nome: 'Ana', idade: 25 },
{ nome: 'João', idade: 30 }
]);
// Grupo
console.group('Validação');
console.log('Campo nome: OK');
console.log('Campo email: INVÁLIDO');
console.groupEnd();
// Tempo de execução
console.time('loop');
for (let i = 0; i < 1000000; i++) {}
console.timeEnd('loop');
// Stack trace
console.trace('Rastreando chamada');
// Estilo
console.log('%cErro crítico', 'color: red; font-size: 20px; font-weight: bold');
Breakpoints (DevTools)
// debugger - pausa execução
function calcularTotal(itens) {
debugger; // DevTools pausa aqui
return itens.reduce((acc, item) => {
return acc + item.preco * item.quantidade;
}, 0);
}
# DevTools Tips:
# F12 > Sources > Abrir arquivo > Clique no número da linha
# Watch: adicione variáveis para monitorar
# Call Stack: veja a pilha de chamadas
# Scope: veja variáveis locais e closure
Strict Mode
'use strict';
// Erros que strict mode pega:
// variável sem declaração
// nome = 'João'; // ReferenceError!
// duplicar parâmetros
// function soma(a, a) {} // SyntaxError!
// deletar propriedade não configurável
// delete Object.prototype; // TypeError!
// this global undefined (em vez de window)
function mostrarThis() {
console.log(this); // undefined (strict)
}
ESLint
# Instalar
npm init -y
npm install --save-dev eslint
# Iniciar configuração
npx eslint --init
# Verificar arquivos
npx eslint src/
npx eslint src/app.js --fix
// .eslintrc.json (exemplo)
{
"env": {
"browser": true,
"es2022": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"no-unused-vars": "warn",
"no-console": "off",
"eqeqeq": "error",
"curly": "error"
}
}
Source Maps
<!-- Source maps permitem debugar código original no DevTools -->
<!-- Arquivo .map gerado pelo bundler -->
<script src="app.bundle.js"></script>
<!-- O navegador carrega app.bundle.js.map automaticamente -->
Lab: Validador com Erros Customizados
Crie uma função que valida dados de formulário e lança erros customizados com campos específicos. Use try/catch e console.table.
node validador.js
class ValidationError extends Error {
constructor(campos) {
super('Erro de validação');
this.name = 'ValidationError';
this.campos = campos; // array de { campo, mensagem }
}
}
function validarUsuario(dados) {
const erros = [];
if (!dados.nome || dados.nome.length < 3) {
erros.push({ campo: 'nome', mensagem: 'Mínimo 3 caracteres' });
}
if (!dados.email?.includes('@')) {
erros.push({ campo: 'email', mensagem: 'E-mail inválido' });
}
if (!dados.idade || dados.idade < 18) {
erros.push({ campo: 'idade', mensagem: 'Deve ser maior de 18 anos' });
}
if (erros.length) throw new ValidationError(erros);
return dados;
}
try {
validarUsuario({ nome: 'An', email: 'invalido' });
} catch (erro) {
if (erro instanceof ValidationError) {
console.table(erro.campos);
} else {
console.error(erro);
}
}
Erros não são falhas — são informações. Trate-os adequadamente, crie classes de erro específicas e nunca deixe uma Promise sem catch.