Errors are an unavoidable part of software development. Whether itβs a typo, a failed API call, or unexpected user input, JavaScript errors can break your application if not handled properly.
Good error handling ensures your app is reliable, debuggable, and user-friendly.
In this blog, weβll explore essential error handling patterns in JavaScript, complete with examples and best practices.
πΉ 1. The Classic try...catch
The most common way to handle errors in JavaScript is using try...catch.
function parseJSON(data) {
try {
return JSON.parse(data);
} catch (error) {
console.error("Invalid JSON:", error.message);
return null;
}
}
console.log(parseJSON('{ "name": "Anshul" }')); // β
Works
console.log(parseJSON("invalid-json")); // β Error handled gracefully
π Best Practice:
Always provide a fallback when something goes wrong.
Log errors with context, not just a generic message.
πΉ 2. Using finally
finally is executed regardless of success or failure, useful for cleanup.
function fetchData() {
try {
console.log("Fetching data...");
throw new Error("Network issue!");
} catch (error) {
console.error("Error:", error.message);
} finally {
console.log("Cleanup resources, close connections, etc.");
}
}
fetchData();
π Best Practice:
Use finally to release resources like file handles, database connections, or loading states.
πΉ 3. Error Handling in Async/Await
When using async/await, wrap code in try...catch.
async function getUser() {
try {
const res = await fetch("https://api.example.com/user");
const data = await res.json();
console.log(data);
} catch (error) {
console.error("Failed to fetch user:", error.message);
}
}
getUser();
π Best Practice:
Always assume network calls can fail. Show user-friendly error messages instead of crashing.
πΉ 4. Centralized Error Handling
Instead of scattering try...catch everywhere, use a central handler.
function handleError(error) {
console.error("Global Error Handler:", error.message);
// Send error to monitoring service like Sentry
}
async function safeExecute(fn) {
try {
await fn();
} catch (error) {
handleError(error);
}
}
// Usage
safeExecute(async () => {
throw new Error("Something broke!");
});
π Best Practice:
Centralized handlers make debugging easier and integrate well with logging/monitoring tools.
πΉ 5. Graceful Degradation with Default Values
Sometimes, instead of crashing, fallback to a default value.
function getUserName(user) {
try {
return user.profile.name;
} catch {
return "Guest"; // fallback
}
}
console.log(getUserName({ profile: { name: "Anshul" } })); // "Anshul"
console.log(getUserName(null)); // "Guest"
π Best Practice:
Use fallbacks for non-critical failures (e.g., missing optional fields).
For critical failures, log them properly.
πΉ 6. Error Boundaries in Frontend Apps
In React, Angular, Vue, use error boundaries to prevent full app crashes.
Example in React:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error("Error caught:", error, info);
}
render() {
if (this.state.hasError) {
return <h2>Something went wrong!</h2>;
}
return this.props.children;
}
}
π Best Practice:
Use error boundaries in UI frameworks to catch render-time crashes.
πΉ 7. Custom Error Classes
Create custom error types for better debugging.
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
function validateAge(age) {
if (age < 18) {
throw new ValidationError("Age must be 18+");
}
return true;
}
try {
validateAge(16);
} catch (error) {
if (error instanceof ValidationError) {
console.error("Validation failed:", error.message);
} else {
throw error;
}
}
π Best Practice:
Use custom errors for business logic checks (e.g., validation, permissions).
β Final Best Practices for Error Handling in JavaScript
πΈ Donβt swallow errors silently β always log them.
πΈ Use meaningful error messages with context.
πΈ Centralize error handling for maintainability.
πΈ Use custom error classes for better debugging.
πΈ Integrate with monitoring tools (Sentry, LogRocket, etc.).
πΈ Show user-friendly messages, not stack traces.
π Conclusion
Error handling is not just about preventing crashesβitβs about building resilient, debuggable, and user-friendly applications.
By applying these patterns and best practices, youβll write cleaner, safer JavaScript code that handles the unexpected gracefully.
Top comments (0)