13. Error Handling
Types of Errors
Syntax Errors
// These occur during parsing/compilation
// console.log("Hello World") // Missing semicolon - SyntaxError
Runtime Errors
// These occur during execution
// console.log(undefinedVariable); // ReferenceError
// console.log("Hello".toUpperCase()); // This works
// (42).toUpperCase(); // TypeError
Logical Errors
// These are bugs in logic that don't throw errors
function add(a, b) {
return a - b; // Should be +, but no error thrown
}
try...catch Statement
try {
// Code that might throw an error
const result = riskyOperation();
console.log("Success:", result);
} catch (error) {
// Handle the error
console.log("Error occurred:", error.message);
} finally {
// Always executes
console.log("Cleanup code here");
}
Error Objects
Built-in Error Types
// ReferenceError
try {
console.log(undefinedVariable);
} catch (error) {
console.log(error.name); // "ReferenceError"
console.log(error.message); // "undefinedVariable is not defined"
}
// TypeError
try {
null.toString();
} catch (error) {
console.log(error.name); // "TypeError"
console.log(error.message); // "Cannot read property 'toString' of null"
}
// SyntaxError (usually caught at parse time)
// const obj = { a: 1, b: 2, }; // Trailing comma is OK in objects
// RangeError
try {
const arr = new Array(-1);
} catch (error) {
console.log(error.name); // "RangeError"
console.log(error.message); // "Invalid array length"
}
// URIError
try {
decodeURIComponent("%");
} catch (error) {
console.log(error.name); // "URIError"
console.log(error.message); // "URI malformed"
}
Custom Errors
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = "ValidationError";
this.field = field;
}
}
class NetworkError extends Error {
constructor(message, statusCode) {
super(message);
this.name = "NetworkError";
this.statusCode = statusCode;
}
}
function validateUser(user) {
if (!user.name) {
throw new ValidationError("Name is required", "name");
}
if (user.age < 0) {
throw new ValidationError("Age cannot be negative", "age");
}
}
try {
validateUser({ name: "", age: -5 });
} catch (error) {
if (error instanceof ValidationError) {
console.log(`Validation failed for ${error.field}: ${error.message}`);
} else {
console.log("Unexpected error:", error.message);
}
}
throw Statement
function divide(a, b) {
if (b === 0) {
throw new Error("Division by zero is not allowed");
}
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("Both arguments must be numbers");
}
return a / b;
}
try {
console.log(divide(10, 0));
} catch (error) {
console.log("Error:", error.message);
}
try {
console.log(divide(10, "2"));
} catch (error) {
console.log("Type error:", error.message);
}
Error Propagation
function level1() {
level2();
}
function level2() {
level3();
}
function level3() {
throw new Error("Something went wrong in level3");
}
try {
level1();
} catch (error) {
console.log("Caught error:", error.message);
console.log("Stack trace:", error.stack);
}
finally Block
function readFile(fileName) {
let fileHandle;
try {
fileHandle = openFile(fileName);
const content = readContent(fileHandle);
return content;
} catch (error) {
console.log("Error reading file:", error.message);
throw error; // Re-throw the error
} finally {
// Always close the file, even if an error occurred
if (fileHandle) {
closeFile(fileHandle);
console.log("File closed");
}
}
}
Async Error Handling
Promises
function fetchUserData(userId) {
return fetch(`/api/users/${userId}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
console.log("Fetch error:", error.message);
throw error; // Re-throw to propagate
});
}
// Usage
fetchUserData(123)
.then(user => console.log(user))
.catch(error => console.log("Final error:", error.message));
Async/Await
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const userData = await response.json();
return userData;
} catch (error) {
console.log("Error fetching user data:", error.message);
throw error;
}
}
// Usage
async function main() {
try {
const user = await fetchUserData(123);
console.log(user);
} catch (error) {
console.log("Failed to get user data:", error.message);
}
}
main();
Best Practices
1. Use Specific Error Types
class DatabaseError extends Error {
constructor(message, code) {
super(message);
this.name = "DatabaseError";
this.code = code;
}
}
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = "ValidationError";
this.field = field;
}
}
2. Don't Ignore Errors
// Bad
try {
riskyOperation();
} catch (error) {
// Ignoring the error
}
// Good
try {
riskyOperation();
} catch (error) {
console.error("Operation failed:", error);
// Handle the error appropriately
}
3. Use finally for Cleanup
function processFile(fileName) {
let file;
try {
file = openFile(fileName);
const data = processData(file);
saveResults(data);
} finally {
if (file) {
file.close();
}
}
}
4. Handle Errors at the Right Level
// Low-level function
function parseJSON(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
throw new Error(`Invalid JSON: ${error.message}`);
}
}
// High-level function
function loadUserConfig() {
try {
const configText = readConfigFile();
const config = parseJSON(configText);
return config;
} catch (error) {
// Provide user-friendly error message
console.error("Failed to load user configuration:", error.message);
return getDefaultConfig();
}
}
5. Log Errors Appropriately
function handleError(error, context = {}) {
const errorInfo = {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
context: context
};
// Log to console in development
if (process.env.NODE_ENV === 'development') {
console.error('Error occurred:', errorInfo);
}
// Send to error reporting service in production
// errorReportingService.send(errorInfo);
}
Next Steps
Proper error handling makes your code more robust and maintainable. Next, let's explore asynchronous programming, which is crucial for modern web development.