阿里云主机折上折
  • 微信号
Current Site:Index > Error handling mechanism

Error handling mechanism

Author:Chuan Chen 阅读数:40548人阅读 分类: JavaScript

The Importance of Error Handling Mechanisms

Error handling is an indispensable part of programming. A robust error handling mechanism enhances code reliability and maintainability. As a dynamically-typed and weakly-typed language, JavaScript is more prone to runtime errors, making a well-designed error handling strategy particularly important.

Classification of Error Types

Errors in JavaScript can be broadly categorized into the following types:

  1. SyntaxError
    Occurs when code violates syntax rules, typically detected during parsing.
// SyntaxError example
const obj = {;
  1. ReferenceError
    Thrown when accessing an undeclared variable.
// ReferenceError example
console.log(undeclaredVar);
  1. TypeError
    Occurs when an operation is performed on an unexpected type.
// TypeError example
const num = 123;
num.toUpperCase();
  1. RangeError
    Thrown when a numeric value exceeds its allowed range.
// RangeError example
new Array(-1);
  1. URIError
    Occurs when URI handling functions are used incorrectly.
// URIError example
decodeURIComponent('%');
  1. Custom Errors
    Error types created by developers based on business requirements.

Basic Structure of try-catch-finally

The most fundamental error handling structure consists of try, catch, and finally blocks:

try {
  // Code that may throw an error
  riskyOperation();
} catch (error) {
  // Error handling logic
  console.error('Operation failed:', error.message);
} finally {
  // Code that executes regardless of errors
  cleanupResources();
}

Special Characteristics of the finally Block

The finally block executes whether or not an error occurs, making it ideal for resource cleanup:

let connection;
try {
  connection = openDatabaseConnection();
  // Perform operations with the connection
} catch (error) {
  logError(error);
} finally {
  if (connection) {
    connection.close();
  }
}

Error Object and Its Properties

The Error object in JavaScript includes the following key properties:

  • name: The type of error
  • message: A description of the error
  • stack: Stack trace (non-standard but widely supported)
try {
  throw new Error('Custom error message');
} catch (err) {
  console.log(err.name);    // "Error"
  console.log(err.message); // "Custom error message"
  console.log(err.stack);   // Stack trace information
}

Best Practices for Throwing Errors

Proactively throwing errors helps expose issues early:

function divide(a, b) {
  if (b === 0) {
    throw new Error('Divisor cannot be zero');
  }
  return a / b;
}

try {
  const result = divide(10, 0);
} catch (error) {
  console.error(error.message); // "Divisor cannot be zero"
}

Custom Error Types

Creating specific error types aids in categorizing and handling errors:

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

function validateInput(input) {
  if (!input) {
    throw new ValidationError('Input cannot be empty');
  }
}

Error Handling in Promises

Errors in Promise chains must be handled using the catch method:

fetchData()
  .then(processData)
  .then(displayData)
  .catch(error => {
    console.error('Process failed:', error);
  });

Error Handling in async/await

Traditional try-catch structures can be used in async functions:

async function loadData() {
  try {
    const data = await fetch('/api/data');
    return process(data);
  } catch (error) {
    console.error('Data loading failed:', error);
    throw error; // Optionally rethrow
  }
}

Global Error Handling

window.onerror

Catches unhandled runtime errors:

window.onerror = function(message, source, lineno, colno, error) {
  console.error(`Global error: ${message} at ${source}:${lineno}`);
  return true; // Prevent default error handling
};

unhandledrejection Event

Handles uncaught Promise rejections:

window.addEventListener('unhandledrejection', event => {
  console.error('Unhandled Promise rejection:', event.reason);
  event.preventDefault(); // Prevent default handling
});

Error Boundaries (React-Specific)

React 16+ introduced the concept of error boundaries:

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, info) {
    logErrorToService(error, info.componentStack);
  }
  
  render() {
    if (this.state.hasError) {
      return <FallbackUI />;
    }
    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

Error Logging

Production environments should log error information:

function logError(error) {
  const errorInfo = {
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent
  };
  
  // Send to error monitoring service
  fetch('/api/log-error', {
    method: 'POST',
    body: JSON.stringify(errorInfo)
  });
}

Defensive Programming Techniques

  1. Parameter Validation
function createUser(name, age) {
  if (typeof name !== 'string' || name.trim() === '') {
    throw new TypeError('Name must be a non-empty string');
  }
  if (!Number.isInteger(age) || age < 0) {
    throw new RangeError('Age must be a positive integer');
  }
  // Normal logic
}
  1. Default Values and Short-Circuiting
function getConfig(options) {
  const config = {
    timeout: options.timeout || 5000,
    retries: options.retries ?? 3  // Default only if undefined or null
  };
  return config;
}
  1. Optional Chaining (?.)
const street = user?.address?.street; // Won't throw an error
  1. Nullish Coalescing Operator (??)
const pageSize = config.pageSize ?? 10; // Default only if null or undefined

Performance Considerations

Error handling can impact performance; keep these in mind:

  1. Avoid try-catch in Hot Code Paths
// Not recommended
function processItems(items) {
  items.forEach(item => {
    try {
      transform(item);
    } catch {
      // Handle error
    }
  });
}

// Recommended
function processItems(items) {
  try {
    items.forEach(transform);
  } catch {
    // Handle error
  }
}
  1. Cost of Error Object Creation

Creating an Error object captures a stack trace, which has performance overhead:

// For performance-critical code, consider simple values
throw { message: 'Simple error' };

Error Handling in Testing

Unit tests should verify error cases:

describe('divide function', () => {
  it('should return correct result when divisor is non-zero', () => {
    expect(divide(10, 2)).toBe(5);
  });
  
  it('should throw error when divisor is zero', () => {
    expect(() => divide(10, 0)).toThrow('Divisor cannot be zero');
  });
});

Browser Compatibility Handling

Some APIs behave differently across browsers:

function getLocalStorage() {
  try {
    localStorage.setItem('test', 'test');
    localStorage.removeItem('test');
    return localStorage;
  } catch (e) {
    // May throw in private mode
    return {
      getItem: () => null,
      setItem: () => {},
      removeItem: () => {}
    };
  }
}

Node.js Environment Differences

Error handling in Node.js has some unique aspects:

// Handle uncaught exceptions
process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err);
  process.exit(1); // Usually exit the process
});

// Handle unhandled Promise rejections
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled rejection:', reason);
});

Error Handling and Code Readability

Good error handling should not sacrifice code readability:

// Not recommended: Deep nesting
function processData(data) {
  try {
    if (data) {
      try {
        const parsed = JSON.parse(data);
        if (parsed.valid) {
          // Business logic
        }
      } catch (parseError) {
        // Handle parse error
      }
    }
  } catch (error) {
    // Handle other errors
  }
}

// Recommended: Flat structure
function processData(data) {
  if (!data) return;
  
  let parsed;
  try {
    parsed = JSON.parse(data);
  } catch (error) {
    handleParseError(error);
    return;
  }
  
  if (!parsed.valid) return;
  
  // Business logic
}

Error Recovery Strategies

Adopt different recovery measures based on error types:

  1. Retry Mechanism
async function fetchWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url);
      return await response.json();
    } catch (error) {
      if (i === retries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}
  1. Fallback Solutions
async function getHighQualityData() {
  try {
    return await fetch('/api/high-quality');
  } catch (error) {
    console.warn('Failed to fetch high-quality data, using low-quality');
    return await fetch('/api/low-quality');
  }
}

User-Friendly Error Messages

When displaying errors to end users, consider:

function showUserError(error) {
  const userMessages = {
    'NetworkError': 'Network connection failed. Check your settings.',
    'InvalidInput': 'Invalid input format',
    'default': 'Operation failed. Please try again later.'
  };
  
  const message = userMessages[error.name] || userMessages.default;
  displayToast(message);
  
  // Also log the full error
  logError(error);
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.