Error Handling Patterns

Proper error handling is crucial for building robust JavaScript applications. Understanding different error handling patterns helps create more reliable and maintainable code.

Try-Catch Basics

// Basic try-catch
try {
  // Code that might throw an error
  throw new Error('Something went wrong');
} catch (error) {
  console.error(error.message);
} finally {
  // Always executes
  cleanup();
}

// Async try-catch
async function fetchData() {
  try {
    const response = await fetch(url);
    const data = await response.json();
    return data;
  } catch (error) {
    if (error instanceof TypeError) {
      // Handle network errors
    }
    throw error; // Re-throw if can't handle
  }
}

Custom Error Types

// Custom Error classes
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

class DatabaseError extends Error {
  constructor(message, query) {
    super(message);
    this.name = 'DatabaseError';
    this.query = query;
  }
}

// Usage
try {
  if (!isValid(data)) {
    throw new ValidationError('Invalid data');
  }
} catch (error) {
  if (error instanceof ValidationError) {
    // Handle validation errors
  } else {
    // Handle other errors
  }
}

Error Handling Patterns

// Error boundary pattern
class ErrorBoundary {
  constructor(errorHandler) {
    this.errorHandler = errorHandler;
  }

  execute(fn) {
    try {
      return fn();
    } catch (error) {
      return this.errorHandler(error);
    }
  }
}

// Result type pattern
class Result {
  constructor(value, error) {
    this.value = value;
    this.error = error;
  }

  static success(value) {
    return new Result(value, null);
  }

  static failure(error) {
    return new Result(null, error);
  }
}

// Usage
function divide(a, b) {
  if (b === 0) {
    return Result.failure(new Error('Division by zero'));
  }
  return Result.success(a / b);
}

Async Error Handling

// Promise error handling
fetchData()
  .then(handleSuccess)
  .catch(handleError)
  .finally(cleanup);

// Global error handling
window.onerror = function(msg, url, line, col, error) {
  // Log to monitoring service
  logError({ msg, url, line, col, error });
  return false;
};

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

Common Interview Follow-up Questions

  1. How do you handle errors in async/await code?
  2. What's the difference between throw and reject?
  3. How do you implement error boundaries in React?
  4. When should you create custom error types?

Best Practices

  • Always catch specific errors first
  • Use custom error types for better error handling
  • Implement proper error logging
  • Don't swallow errors silently
  • Consider using error boundaries
  • Handle both sync and async errors