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

Error handling mechanism

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

Error Handling Mechanism

The error handling mechanism in JavaScript is a crucial part of ensuring code robustness. When exceptions occur during code execution, without proper handling mechanisms, the program may crash or produce unexpected behavior. Through reasonable error handling, these exceptions can be captured and managed, ensuring the program can continue gracefully or provide meaningful feedback.

try-catch Statement

try-catch is the most basic error handling structure. The try block contains code that might throw an error, while the catch block captures and handles these errors.

try {
  // Code that might throw an error
  const result = someFunctionThatMightFail();
  console.log(result);
} catch (error) {
  // Handle the error
  console.error('An error occurred:', error.message);
}

The catch block receives an error object, typically containing a message property describing the error details. You can also access the name and stack properties to get the error type and call stack information.

finally Block

The finally block executes regardless of whether an error occurs, often used for resource cleanup.

let fileHandle;
try {
  fileHandle = openFile('example.txt');
  processFile(fileHandle);
} catch (error) {
  console.error('File processing failed:', error);
} finally {
  if (fileHandle) {
    closeFile(fileHandle);
  }
}

Error Types

JavaScript has several built-in error types, which can be checked using instanceof:

try {
  // Code that might throw different types of errors
} catch (error) {
  if (error instanceof TypeError) {
    console.error('Type error:', error.message);
  } else if (error instanceof ReferenceError) {
    console.error('Reference error:', error.message);
  } else {
    console.error('Unknown error:', error.message);
  }
}

Common built-in error types include:

  • Error: Generic base error class
  • SyntaxError: Syntax error
  • TypeError: Type error
  • ReferenceError: Reference error
  • RangeError: Numeric value out of range
  • URIError: URI handling error
  • EvalError: eval function error

Custom Errors

You can create custom error types by extending the Error class:

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

function validateUser(user) {
  if (!user.name) {
    throw new ValidationError('Name is required', 'name');
  }
  if (!user.email) {
    throw new ValidationError('Email is required', 'email');
  }
}

try {
  validateUser({});
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(`Validation failed for ${error.field}: ${error.message}`);
  } else {
    console.error('Unknown error:', error);
  }
}

throw Statement

Use throw to actively throw an error. While any value can be thrown, the best practice is to throw an instance of Error or its subclasses.

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

try {
  const result = divide(10, 0);
  console.log(result);
} catch (error) {
  console.error('Division error:', error.message);
}

Promise Error Handling

Promises use the .catch() method to handle errors or try-catch with async/await.

// Using .catch()
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Fetch failed:', error));

// Using async/await
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Fetch failed:', error);
  }
}

Global Error Handling

window.onerror

Captures unhandled runtime errors:

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

unhandledrejection Event

Captures unhandled Promise rejections:

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

Error Boundaries (React)

In React, error boundaries are components that catch JavaScript errors in their child component tree:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    logErrorToService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

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

Error Logging

In production environments, errors should be logged to a server:

function logError(error) {
  const errorData = {
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent,
    url: window.location.href
  };

  fetch('/api/log-error', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(errorData)
  }).catch(loggingError => {
    console.error('Failed to log error:', loggingError);
  });
}

window.addEventListener('error', event => {
  logError(event.error);
});

window.addEventListener('unhandledrejection', event => {
  logError(event.reason);
});

Defensive Programming

Defensive programming practices combined with error handling:

// 1. Parameter validation
function createUser(userData) {
  if (!userData || typeof userData !== 'object') {
    throw new TypeError('User data must be an object');
  }
  // ...
}

// 2. Default values and optional chaining
function getAddress(user) {
  return user?.address?.street ?? 'Unknown';
}

// 3. Nullish coalescing
const config = userConfig ?? defaultConfig;

// 4. Type checking
function calculateTotal(items) {
  if (!Array.isArray(items)) {
    throw new TypeError('Items must be an array');
  }
  // ...
}

Error Handling in Async Iterators

Special attention is needed when handling errors in async iterators:

async function processAsyncIterable(asyncIterable) {
  try {
    for await (const item of asyncIterable) {
      console.log(item);
    }
  } catch (error) {
    console.error('Async iteration failed:', error);
  }
}

// Example usage
const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      async next() {
        if (i++ < 3) {
          await new Promise(resolve => setTimeout(resolve, 100));
          return { value: i, done: false };
        }
        throw new Error('Async iteration error');
      }
    };
  }
};

processAsyncIterable(asyncIterable);

Error Handling in Node.js

Node.js has some specific error handling patterns:

// 1. Error-first callback convention
fs.readFile('file.txt', (err, data) => {
  if (err) {
    console.error('Error reading file:', err);
    return;
  }
  console.log(data.toString());
});

// 2. EventEmitter error handling
const eventEmitter = new EventEmitter();
eventEmitter.on('error', err => {
  console.error('Emitter error:', err);
});

// 3. Using the domain module (deprecated, but important to understand the concept)

Performance Considerations

Impact of error handling on performance:

  1. try-catch blocks have minimal performance overhead when no errors are thrown
  2. Frequently throwing and catching errors can impact performance
  3. Avoid complex error handling in hot code paths
  4. Minimize the data volume of error logs in production
// Bad practice: Using try-catch inside a loop
for (let i = 0; i < 1000000; i++) {
  try {
    riskyOperation();
  } catch (e) {
    // ...
  }
}

// Better practice: Wrap the entire loop in try-catch
try {
  for (let i = 0; i < 1000000; i++) {
    riskyOperation();
  }
} catch (e) {
  // ...
}

Testing Error Scenarios

Write tests to verify error handling logic:

// Using Jest to test error throwing
test('divide throws error when dividing by zero', () => {
  expect(() => divide(10, 0)).toThrow('Division by zero');
  expect(() => divide(10, 0)).toThrow(Error);
});

// Testing async errors
test('async function rejects properly', async () => {
  await expect(fetchData('invalid-url')).rejects.toThrow('Network error');
});

// Testing React error boundaries
test('ErrorBoundary catches child errors', () => {
  const ErrorComponent = () => {
    throw new Error('Test error');
  };
  
  const wrapper = mount(
    <ErrorBoundary>
      <ErrorComponent />
    </ErrorBoundary>
  );
  
  expect(wrapper.text()).toContain('Something went wrong');
});

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

如果侵犯了你的权益请来信告知我们删除。邮箱: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 ☕.