Error handling mechanism
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:
- SyntaxError
Occurs when code violates syntax rules, typically detected during parsing.
// SyntaxError example
const obj = {;
- ReferenceError
Thrown when accessing an undeclared variable.
// ReferenceError example
console.log(undeclaredVar);
- TypeError
Occurs when an operation is performed on an unexpected type.
// TypeError example
const num = 123;
num.toUpperCase();
- RangeError
Thrown when a numeric value exceeds its allowed range.
// RangeError example
new Array(-1);
- URIError
Occurs when URI handling functions are used incorrectly.
// URIError example
decodeURIComponent('%');
- 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
- 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
}
- 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;
}
- Optional Chaining (?.)
const street = user?.address?.street; // Won't throw an error
- 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:
- 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
}
}
- 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:
- 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)));
}
}
}
- 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
下一篇:模块化开发规范