DevToolBoxFREE
BlogAdvertise

Tratamento de Erros JavaScript: try/catch, erros async, erros personalizados

11 minby DevToolBox

O tratamento robusto de erros distingue o JavaScript pronto para produção dos demos frágeis. Este guia cobre os padrões try/catch, o tratamento de erros async e as classes de erro personalizadas.

Fundamentos de try/catch

O bloco try/catch/finally é a base do tratamento síncrono de erros em JavaScript.

// try/catch/finally fundamentals

// Basic structure
try {
  const data = JSON.parse(userInput); // Can throw SyntaxError
  processData(data);
} catch (error) {
  if (error instanceof SyntaxError) {
    console.error('Invalid JSON:', error.message);
  } else {
    throw error; // Re-throw errors you can't handle here
  }
} finally {
  cleanup(); // Always runs — use for resource cleanup
}

// What catch does NOT catch:
// 1. Errors in async callbacks (use async/await instead)
// 2. Errors in setTimeout/setInterval
// 3. Errors in event handlers

// Wrong: setTimeout error is not caught
try {
  setTimeout(() => {
    throw new Error('This is NOT caught by outer try/catch');
  }, 100);
} catch (e) {
  // This never runs
}

// Right: wrap async code
setTimeout(() => {
  try {
    throw new Error('This IS caught');
  } catch (e) {
    console.error(e);
  }
}, 100);

// Distinguishing error types
function handleError(error) {
  if (error instanceof TypeError) {
    // Null dereference, wrong type
  } else if (error instanceof RangeError) {
    // Array out of bounds, recursion limit
  } else if (error instanceof NetworkError) {
    // Custom: network failure
  } else {
    // Unknown: re-throw
    throw error;
  }
}

Tratamento de erros async/await

Funções async retornam Promises, então erros devem ser capturados de forma diferente.

// Async/Await Error Handling

// 1. Basic async error handling
async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    return await response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Request was aborted');
      return null;
    }
    throw error; // Re-throw for caller to handle
  }
}

// 2. Handling multiple async operations
async function loadDashboard(userId) {
  // Run in parallel, catch errors individually
  const [user, posts, stats] = await Promise.allSettled([
    fetchUser(userId),
    fetchPosts(userId),
    fetchStats(userId),
  ]);

  return {
    user: user.status === 'fulfilled' ? user.value : null,
    posts: posts.status === 'fulfilled' ? posts.value : [],
    stats: stats.status === 'fulfilled' ? stats.value : {},
    errors: [user, posts, stats]
      .filter(r => r.status === 'rejected')
      .map(r => r.reason),
  };
}

// 3. Async error handling utility
async function tryCatchAsync<T>(
  promise: Promise<T>
): Promise<[T | null, Error | null]> {
  try {
    const data = await promise;
    return [data, null];
  } catch (error) {
    return [null, error instanceof Error ? error : new Error(String(error))];
  }
}

// Usage: avoids nested try/catch
const [user, error] = await tryCatchAsync(fetchUser(id));
if (error) {
  console.error('Failed to fetch user:', error.message);
  return;
}
console.log(user.name);

Classes de erro personalizadas

Classes de erro personalizadas permitem tratamento estruturado de erros.

// Custom Error Classes

class AppError extends Error {
  public readonly code: string;
  public readonly statusCode: number;
  public readonly isOperational: boolean;

  constructor(
    message: string,
    code: string,
    statusCode = 500,
    isOperational = true
  ) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.statusCode = statusCode;
    this.isOperational = isOperational;

    // Maintains proper prototype chain (important for instanceof)
    Object.setPrototypeOf(this, new.target.prototype);

    // Captures stack trace (V8 only)
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.constructor);
    }
  }
}

class ValidationError extends AppError {
  public readonly fields: Record<string, string[]>;

  constructor(message: string, fields: Record<string, string[]> = {}) {
    super(message, 'VALIDATION_ERROR', 400);
    this.fields = fields;
  }
}

class NotFoundError extends AppError {
  constructor(resource: string, id: string | number) {
    super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
  }
}

class NetworkError extends AppError {
  public readonly url: string;

  constructor(message: string, url: string) {
    super(message, 'NETWORK_ERROR', 503);
    this.url = url;
  }
}

// Usage
function findUser(id: number) {
  const user = db.find(id);
  if (!user) throw new NotFoundError('User', id);
  return user;
}

// Error type checking
try {
  findUser(999);
} catch (error) {
  if (error instanceof NotFoundError) {
    res.status(404).json({ error: error.message, code: error.code });
  } else if (error instanceof ValidationError) {
    res.status(400).json({ error: error.message, fields: error.fields });
  } else {
    // Unknown error — log and return 500
    logger.error('Unexpected error', { error });
    res.status(500).json({ error: 'Internal server error' });
  }
}

Manipuladores de erros globais

Alguns erros escapam dos blocos try/catch locais. Manipuladores globais são sua última linha de defesa.

// Global Error Handlers

// Browser: uncaught synchronous errors
window.onerror = (message, source, lineno, colno, error) => {
  console.error('Uncaught error:', { message, source, lineno, colno, error });
  reportToSentry(error);
  return true; // Prevents default browser error dialog
};

// Browser: unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason);
  reportToSentry(event.reason);
  event.preventDefault(); // Prevents console error
});

// Node.js: uncaught exceptions
process.on('uncaughtException', (error) => {
  console.error('Uncaught exception:', error);
  reportToSentry(error);
  // Exit after logging — cannot safely continue after uncaughtException
  process.exit(1);
});

// Node.js: unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled rejection at:', promise, 'reason:', reason);
  reportToSentry(reason instanceof Error ? reason : new Error(String(reason)));
});

// Express.js: error middleware (must have 4 params)
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const isOperational = err.isOperational || false;

  if (!isOperational) {
    // Unexpected error — log full details
    console.error('Unexpected error:', err);
    reportToSentry(err, { url: req.url, method: req.method });
  }

  res.status(statusCode).json({
    error: {
      message: isOperational ? err.message : 'Internal server error',
      code: err.code || 'INTERNAL_ERROR',
    },
  });
});

Perguntas frequentes

Devo capturar cada erro?

Não. Capture apenas erros que você possa tratar significativamente no nível atual.

Qual a diferença entre throw e throw new Error()?

Lançar objetos não-Error perde o stack trace. Sempre use new Error().

Como tratar erros em Promise.all()?

Promise.all() rejeita assim que qualquer promise é rejeitada. Use Promise.allSettled() para todos os resultados.

O que incluir nas mensagens de erro?

As mensagens devem ser específicas. Nunca inclua dados sensíveis.

Ferramentas relacionadas

Isso foi útil?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy