DevToolBoxFREE
BlogAdvertise

Gestion des Erreurs JavaScript: try/catch, erreurs async, erreurs personnalisées

11 minpar DevToolBox

La gestion robuste des erreurs distingue le JavaScript prêt pour la production des démos fragiles. Ce guide couvre les patterns try/catch, la gestion des erreurs async, les classes d'erreur personnalisées.

Fondamentaux de try/catch

Le bloc try/catch/finally est la base de la gestion synchrone des erreurs en 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;
  }
}

Gestion des erreurs async/await

Les fonctions async retournent des Promises, donc les erreurs doivent être capturées différemment.

// 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 d'erreur personnalisées

Les classes d'erreur personnalisées permettent une gestion structurée des erreurs et des traces de pile meilleures.

// 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' });
  }
}

Gestionnaires d'erreurs globaux

Certaines erreurs passent à travers les blocs try/catch locaux. Les gestionnaires globaux sont votre dernier recours.

// 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',
    },
  });
});

Questions fréquentes

Dois-je capturer chaque erreur ?

Non. Capturez uniquement les erreurs que vous pouvez gérer de manière significative au niveau actuel.

Quelle est la différence entre throw et throw new Error() ?

Vous pouvez lancer n'importe quelle valeur, mais lancer des non-Error perd la trace de pile. Utilisez toujours new Error().

Comment gérer les erreurs dans Promise.all() ?

Promise.all() rejette dès qu'une promesse est rejetée. Utilisez Promise.allSettled() pour obtenir tous les résultats.

Que dois-je inclure dans les messages d'erreur ?

Les messages d'erreur doivent être exploitables et spécifiques. N'incluez jamais de données sensibles.

Outils associés

Cet article vous a-t-il aidé ?

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

Essayez ces outils associés

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