Error Handling (try…catch…finally)
basic
Error handling is a crucial aspect of writing robust JavaScript applications. This guide explores how to effectively use try…catch…finally blocks and implement proper error handling strategies.
Basic Error Handling
1. Try…Catch Basics
try {
// Code that might throw an error
throw new Error('Something went wrong');
} catch (error) {
console.error('Error occurred:', error.message);
} finally {
// Code that always runs
console.log('This always executes');
}
// Practical example
function divideNumbers(a, b) {
try {
if (b === 0) {
throw new Error('Division by zero is not allowed');
}
return a / b;
} catch (error) {
console.error('Division error:', error.message);
return null;
}
}2. Error Types
// Built-in JavaScript errors
try {
// TypeError
null.toString();
} catch (error) {
if (error instanceof TypeError) {
console.log('Type error occurred');
}
}
try {
// ReferenceError
nonExistentVariable;
} catch (error) {
if (error instanceof ReferenceError) {
console.log('Reference error occurred');
}
}
try {
// SyntaxError (Note: Cannot be caught if in the same scope)
eval('Invalid JavaScript');
} catch (error) {
if (error instanceof SyntaxError) {
console.log('Syntax error occurred');
}
}Custom Error Classes
1. Creating Custom Errors
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
function validateUser(user) {
try {
if (!user.name) {
throw new ValidationError('Name is required');
}
if (!user.email) {
throw new ValidationError('Email is required');
}
} catch (error) {
if (error instanceof ValidationError) {
console.error('Validation failed:', error.message);
} else {
throw error; // Re-throw unexpected errors
}
}
}2. Error Hierarchies
// Base error class for application
class AppError extends Error {
constructor(message, status) {
super(message);
this.name = this.constructor.name;
this.status = status;
Error.captureStackTrace(this, this.constructor);
}
}
// Specific error types
class HttpError extends AppError {
constructor(message, status = 500) {
super(message, status);
}
}
class ValidationError extends AppError {
constructor(message, field) {
super(message, 400);
this.field = field;
}
}
// Usage
try {
throw new HttpError('Not Found', 404);
} catch (error) {
if (error instanceof HttpError) {
console.error(`${error.status}: ${error.message}`);
}
}Async Error Handling
1. Promises
// Using .catch with promises
fetchData()
.then(data => processData(data))
.catch(error => {
console.error('Error fetching data:', error);
});
// Chaining multiple catches
fetchData()
.then(data => processData(data))
.catch(error => {
if (error instanceof NetworkError) {
return fetchBackupData();
}
throw error;
})
.then(data => displayData(data))
.catch(error => {
console.error('Unrecoverable error:', error);
});2. Async/Await
async function fetchUserData(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchUserPosts(user.id);
return { user, posts };
} catch (error) {
console.error('Error fetching user data:', error);
throw error; // Re-throw if needed
} finally {
// Cleanup code
}
}
// Multiple try-catch blocks
async function processUserData(userId) {
try {
const user = await fetchUser(userId);
try {
await validateUser(user);
} catch (validationError) {
console.error('Validation failed:', validationError);
return null;
}
return user;
} catch (error) {
console.error('Error processing user:', error);
throw error;
}
}Error Handling Patterns
1. Error Wrapper Function
function withErrorHandling(fn) {
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
console.error(`Error in ${fn.name}:`, error);
throw error;
}
};
}
// Usage
const safeOperation = withErrorHandling(async function riskyOperation() {
// Potentially risky code
});2. Error Handler Class
class ErrorHandler {
static handle(error, context = '') {
if (error instanceof ValidationError) {
this.handleValidationError(error, context);
} else if (error instanceof DatabaseError) {
this.handleDatabaseError(error, context);
} else {
this.handleUnknownError(error, context);
}
}
static handleValidationError(error, context) {
console.error(`Validation error in ${context}:`, error.message);
// Additional handling logic
}
static handleDatabaseError(error, context) {
console.error(`Database error in ${context}:`, error.message);
// Additional handling logic
}
static handleUnknownError(error, context) {
console.error(`Unknown error in ${context}:`, error);
// Additional handling logic
}
}
// Usage
try {
// Risky operation
} catch (error) {
ErrorHandler.handle(error, 'UserService');
}3. Result Type Pattern
class Result {
constructor(success, data = null, error = null) {
this.success = success;
this.data = data;
this.error = error;
}
static ok(data) {
return new Result(true, data);
}
static fail(error) {
return new Result(false, null, error);
}
}
async function fetchUserData(userId) {
try {
const user = await fetchUser(userId);
return Result.ok(user);
} catch (error) {
return Result.fail(error);
}
}
// Usage
const result = await fetchUserData(123);
if (result.success) {
console.log('User:', result.data);
} else {
console.error('Error:', result.error);
}Best Practices
- Specific Error Handling
try {
await saveUser(user);
} catch (error) {
if (error instanceof ValidationError) {
// Handle validation errors
} else if (error instanceof DatabaseError) {
// Handle database errors
} else {
// Handle unknown errors
}
}- Cleanup with Finally
let connection;
try {
connection = await database.connect();
await connection.query('SELECT * FROM users');
} catch (error) {
console.error('Database error:', error);
} finally {
if (connection) {
await connection.close();
}
}- Error Recovery Strategies
async function fetchDataWithRetry(url, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fetch(url);
} catch (error) {
if (attempt === maxRetries) throw error;
await delay(1000 * attempt); // Exponential backoff
}
}
}