DevToolBoxKOSTENLOS
Blog

JavaScript Fehlerbehandlung Best Practices

11 minvon DevToolBox

Robuste Fehlerbehandlung unterscheidet produktionsbereites JavaScript von fragilen Demos. Dieser Leitfaden behandelt try/catch-Muster, async-Fehlerbehandlung und benutzerdefinierte Fehlerklassen.

try/catch-Grundlagen

Der try/catch/finally-Block ist die Basis der synchronen Fehlerbehandlung in 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;
  }
}

Async/Await-Fehlerbehandlung

Async-Funktionen geben Promises zurück, daher müssen Fehler anders abgefangen werden.

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

Benutzerdefinierte Fehlerklassen

Benutzerdefinierte Fehlerklassen ermöglichen strukturierte Fehlerbehandlung und bessere Stack-Traces.

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

Globale Fehler-Handler

Manche Fehler gleiten durch lokale try/catch-Blöcke. Globale Handler sind Ihre letzte Verteidigungslinie.

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

Häufig gestellte Fragen

Sollte ich jeden Fehler abfangen?

Nein. Fangen Sie nur Fehler ab, die Sie auf der aktuellen Ebene sinnvoll behandeln können.

Was ist der Unterschied zwischen throw und throw new Error()?

Beim Werfen von Nicht-Error-Objekten geht der Stack-Trace verloren. Verwenden Sie immer new Error().

Wie behandle ich Fehler in Promise.all()?

Promise.all() lehnt ab, sobald eine Promise abgelehnt wird. Verwenden Sie Promise.allSettled() für alle Ergebnisse.

Was soll ich in Fehlermeldungen einschließen?

Fehlermeldungen sollten umsetzbar und spezifisch sein. Niemals sensible Daten einschließen.

Verwandte Tools

𝕏 Twitterin LinkedIn
War das hilfreich?

Bleiben Sie informiert

Wöchentliche Dev-Tipps und neue Tools.

Kein Spam. Jederzeit abbestellbar.

Verwandte Tools ausprobieren

{ }JSON FormatterJSJavaScript Minifier

Verwandte Artikel

JavaScript Promises und Async/Await: Kompletter Guide

JavaScript Promises und async/await meistern: Erstellung, Verkettung, Promise.all und Fehlerbehandlung.

JavaScript Closures Erklärt: Scope, Speicher und Praxismuster

Tiefenanalyse JavaScript Closures 2026: lexikalischer Scope, Speicherauswirkungen, Memoization, Modul-Pattern und Currying.

React Hooks Komplett-Guide

React Hooks meistern.