Error handling pattern
ECMAScript 6 Error Handling Patterns
ECMAScript 6 introduced more robust error handling mechanisms, including Promise exception catching, optimized this
binding in arrow functions, and new Error subclasses. These features make error handling in asynchronous and synchronous code more unified and flexible.
Promise Error Handling
Promises handle errors in asynchronous operations through the .catch()
method. Compared to traditional callback functions, Promise error handling is more intuitive:
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.catch(error => {
console.error('Fetch error:', error);
// Error recovery or reporting can be done here
});
Errors in a Promise chain propagate downward until caught by .catch()
. ES6 also introduced Promise.reject()
to quickly create a rejected Promise:
function getUser(id) {
if (!id) {
return Promise.reject(new Error('Invalid user ID'));
}
return fetch(`/users/${id}`);
}
try-catch with async/await
The async/await syntax allows synchronous-style try-catch blocks for handling errors in asynchronous code:
async function loadData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Failed to load data:', error);
// Error recovery can be performed here
}
}
This pattern is particularly suitable for scenarios requiring sequential processing of multiple asynchronous operations, with more centralized error handling logic.
Error Class Extensions
ES6 allows creating custom error types through class inheritance, which is highly useful in large applications:
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');
}
}
try {
validateUser({});
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Validation failed for ${error.field}:`, error.message);
} else {
console.error('Unexpected error:', error);
}
}
finally Clause
ES6 supports the finally
clause in both Promises and try-catch blocks for executing cleanup code that must run regardless of success or failure:
let isLoading = true;
fetch('/data')
.then(handleData)
.catch(handleError)
.finally(() => {
isLoading = false;
console.log('Request completed');
});
In synchronous code:
let resource;
try {
resource = acquireResource();
// Use the resource
} catch (error) {
console.error(error);
} finally {
if (resource) {
resource.release();
}
}
Global Error Handling
For unhandled Promise rejections, global handlers can be added:
window.addEventListener('unhandledrejection', event => {
console.error('Unhandled rejection:', event.reason);
// Errors can be reported here
event.preventDefault(); // Prevent default browser console error
});
For conventional errors:
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global error:', { message, source, lineno, error });
// Return true to prevent default error prompts
return true;
};
Error Boundary Pattern
In complex applications, the error boundary pattern can isolate component-level errors:
class ErrorBoundary {
constructor(app) {
this.app = app;
}
async run() {
try {
await this.app.start();
} catch (error) {
this.handleError(error);
}
}
handleError(error) {
// Log detailed error information
console.error('Application error:', error);
// Display a user-friendly error interface
showErrorUI();
// Report the error to the server
reportError(error);
}
}
Error Translation Pattern
When dealing with multiple layers of abstraction, the error translation pattern can preserve original error information:
class DataService {
async getData() {
try {
return await fetchData();
} catch (rawError) {
throw new DataServiceError('Failed to fetch data', {
cause: rawError
});
}
}
}
class DataServiceError extends Error {
constructor(message, { cause }) {
super(message);
this.cause = cause;
}
}
Defensive Programming Practices
Combining ES6 features enables more robust defensive programming:
function parseJSONSafely(jsonString) {
try {
return JSON.parse(jsonString);
} catch {
// Use null instead of throwing an error
return null;
}
}
// Use default parameters and nullish coalescing
function createUser({ name = 'Anonymous', age } = {}) {
return {
name,
age: age ?? 18
};
}
Performance Considerations
Error handling significantly impacts performance, especially in hot-path code:
// Avoid frequent try-catch in performance-critical code
function processItems(items) {
// Checking first and then processing is more efficient than direct try-catch
if (!Array.isArray(items)) {
return [];
}
return items.map(item => transformItem(item));
}
Enhanced Debugging
Leverage the stack
property of Error objects to enhance debugging:
function assert(condition, message) {
if (!condition) {
const error = new Error(message || 'Assertion failed');
console.error(error.stack);
throw error;
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn