Proper error handling makes your code robust and easier to debug. JavaScript provides several mechanisms for handling errors. See Javascript Guide for more context. See Javascript Guide for more context.
Try-Catch-Finally
The fundamental error handling structure:
try {
// Code that might throw an error
const result = riskyOperation();
} catch (error) {
// Handle the error
console.log("Error occurred:", error.message);
} finally {
// Runs regardless of success or error
console.log("Cleanup code");
}
Basic Example
try {
const data = JSON.parse("invalid json");
} catch (error) {
console.log("Failed to parse JSON:", error.message);
}
Accessing Error Information
try {
throw new Error("Something went wrong");
} catch (error) {
console.log(error.name); // "Error"
console.log(error.message); // "Something went wrong"
console.log(error.stack); // Full stack trace
}
Error Types
JavaScript has several built-in error types:
SyntaxError
Occurs when code has invalid syntax:
// This would cause a SyntaxError at parse time
// const x = ;
ReferenceError
Accessing undefined variables:
try {
console.log(undefinedVariable);
} catch (error) {
console.log(error instanceof ReferenceError); // true
}
TypeError
Wrong type or invalid operation:
try {
const x = null;
x.property; // TypeError: Cannot read property 'property' of null
} catch (error) {
console.log(error instanceof TypeError); // true
}
RangeError
Value out of valid range:
try {
const arr = new Array(-1); // RangeError
} catch (error) {
console.log(error instanceof RangeError); // true
}
Throwing Errors
Throw Statement
function validateAge(age) {
if (age < 0) {
throw new Error("Age cannot be negative");
}
return age;
}
try {
validateAge(-5);
} catch (error) {
console.log(error.message); // "Age cannot be negative"
}
Throwing Different Types
function processData(data) {
if (!data) {
throw new TypeError("Data is required");
}
if (data.length === 0) {
throw new RangeError("Data cannot be empty");
}
return data;
}
Custom Errors
Create custom error classes for specific situations:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
try {
throw new ValidationError("Invalid email format");
} catch (error) {
if (error instanceof ValidationError) {
console.log("Validation failed:", error.message);
}
}
More Complex Custom Error
class APIError extends Error {
constructor(message, statusCode, response) {
super(message);
this.name = "APIError";
this.statusCode = statusCode;
this.response = response;
}
}
try {
throw new APIError("Not found", 404, { error: "Resource not found" });
} catch (error) {
if (error instanceof APIError) {
console.log(`API Error ${error.statusCode}: ${error.message}`);
}
}
Finally Block
The finally block always executes:
function example() {
try {
console.log("Try block");
return "from try";
} catch (error) {
console.log("Catch block");
return "from catch";
} finally {
console.log("Finally block"); // Always runs
}
}
console.log(example());
// Output:
// Try block
// Finally block
// from try
Cleanup with Finally
function readFile(filename) {
let file;
try {
file = openFile(filename);
return file.read();
} catch (error) {
console.log("Error reading file:", error.message);
} finally {
if (file) {
file.close(); // Always close the file
}
}
}
Practical Examples
Parsing JSON Safely
function parseJSON(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.log("Invalid JSON:", error.message);
return null;
}
}
console.log(parseJSON('{"name": "Alice"}')); // { name: "Alice" }
console.log(parseJSON("invalid")); // null
API Call with Error Handling
async function fetchUser(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.log("Failed to fetch user:", error.message);
return null;
}
}
Validation with Custom Errors
class ValidationError extends Error {
constructor(field, message) {
super(`${field}: ${message}`);
this.name = "ValidationError";
this.field = field;
}
}
function validateUser(user) {
if (!user.name) {
throw new ValidationError("name", "Name is required");
}
if (!user.email) {
throw new ValidationError("email", "Email is required");
}
if (user.age < 18) {
throw new ValidationError("age", "Must be 18 or older");
}
return true;
}
try {
validateUser({ name: "Alice", email: "[email protected]", age: 16 });
} catch (error) {
if (error instanceof ValidationError) {
console.log(`Validation error in ${error.field}: ${error.message}`);
}
}
Retry Logic
async function retryOperation(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) {
throw error; // Last attempt failed
}
console.log(`Attempt ${i + 1} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
// Usage
retryOperation(async () => {
const response = await fetch('/api/data');
return response.json();
});
Error Handling Best Practices
Be Specific
// Good - specific error handling
try {
const data = JSON.parse(jsonString);
} catch (error) {
if (error instanceof SyntaxError) {
console.log("Invalid JSON format");
} else {
console.log("Unexpected error:", error);
}
}
// Avoid - too broad
try {
// lots of code
} catch (error) {
console.log("Something went wrong");
}
Don’t Swallow Errors Silently
// Bad - error is ignored
try {
riskyOperation();
} catch (error) {
// Nothing happens
}
// Good - error is logged or handled
try {
riskyOperation();
} catch (error) {
console.error("Operation failed:", error);
// Take appropriate action
}
Use Finally for Cleanup
// Good - cleanup guaranteed
try {
// operation
} finally {
cleanup();
}
// Avoid - cleanup might not run
try {
// operation
}
catch (error) {
cleanup();
throw error;
}
Provide Context
// Good - context helps debugging
try {
const user = await fetchUser(userId);
} catch (error) {
throw new Error(`Failed to fetch user ${userId}: ${error.message}`);
}
// Less helpful
try {
const user = await fetchUser(userId);
} catch (error) {
throw error;
}
Related Resources
Official Documentation
External Resources
Summary
- Try-catch-finally: fundamental error handling structure
- Error types: SyntaxError, ReferenceError, TypeError, RangeError
- Throw: create errors with
throw new Error() - Custom errors: extend Error class for domain-specific errors
- Finally: guaranteed cleanup code
- Best practices: be specific, don’t swallow errors, provide context
Next Steps
- Debugging JavaScript: Tools and Techniques
- Custom Errors and Error Types
- Async/Await: Modern Asynchronous Programming
- Error Handling with Promises and Async/Await
- Testing Basics with Jest
Comments