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)