Master professional error handling strategies for enterprise-scale applications
💡 Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:
.html file with <script> tags and open it in your browserError handling in small scripts is simple: wrap things in try/catch and log a message. But once you start building real applications — dashboards, ecommerce sites, admin tools, mobile apps, AI tools — everything becomes more complex.
Large applications have:
Instead of wrapping EVERYTHING in try/catch, you wrap logical boundaries.
Wrap logical boundaries instead of wrapping everything
async function initializeApp() {
try {
await loadUser();
await loadDashboard();
await connectToSocket();
} catch (err) {
handleGlobalError(err);
}
}Instead of writing console.error everywhere, use a shared handler.
Use a shared handler instead of console.error everywhere
function handleError(error, context = "") {
console.group(`Error in ${context}`);
console.error(error);
console.groupEnd();
// Optional: send to server
// sendToErrorTracker({ error, context });
}
// Usage:
try {
riskyOp();
} catch (err) {
handleError(err, "riskyOp");
}Instead of assuming everything exists, verify it.
Verify everything exists instead of assuming
const el = document.querySelector("#profile");
if (!el) return; // fail early
// Check API responses
if (!data || !data.user) throw new Error("Malformed response");
// Check input types
if (typeof callback !== "function") return;In big apps, async errors often get lost.
Silent failure when not using try/catch
async function load() {
const data = await fetchData(); // if this rejects → silent failure
}Proper try/catch for async functions
async function load() {
try {
const data = await fetchData();
} catch (err) {
handleError(err, "load");
}
}Even better — wrap async functions in a helper:
Wrap async functions in a helper for Go-style error handling
async function safe(fn) {
try {
return [await fn(), null];
} catch (err) {
return [null, err];
}
}
// Usage:
const [data, error] = await safe(() => fetchData());
// No crashes, no lost errors.Large apps must handle unhandled Promise rejections:
Catch all unhandled Promise rejections
window.addEventListener("unhandledrejection", (event) => {
handleError(event.reason, "Unhandled Promise");
});Allow components to fail without breaking entire UI
class ErrorBoundary extends React.Component {
state = { hasError: false };
componentDidCatch(error, info) {
this.setState({ hasError: true });
}
render() {
return this.state.hasError
? <FallbackUI />
: this.props.children;
}
}Create specific error types for better handling
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
class ApiError extends Error {
constructor(message, status) {
super(message);
this.name = "ApiError";
this.status = status;
}
}
// Usage:
try {
throw new ApiError("Not Found", 404);
} catch (err) {
if (err instanceof ApiError) {
console.error("API error:", err.status);
}
}Exponential backoff doubles wait time between retries
async function retry(fn, retries = 3, delay = 500) {
try {
return await fn();
} catch (err) {
if (retries === 0) throw err;
await new Promise(res => setTimeout(res, delay));
return retry(fn, retries - 1, delay * 2); // exponential backoff
}
}
// Usage:
const data = await retry(() => fetch("/api/data").then(r => r.json()));
// Dramatically reduces user errors for poor mobile or public WiFi networks.Ensure the user always gets feedback with timeouts
function fetchWithTimeout(url, ms = 5000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), ms);
return fetch(url, { signal: controller.signal })
.finally(() => clearTimeout(timer));
}
// Handle timeout:
try {
const res = await fetchWithTimeout("/api/data", 3000);
} catch (error) {
if (error.name === "AbortError") {
console.error("Request timed out");
}
}If an API repeatedly fails, the app temporarily stops calling it:
Stop calling failing APIs temporarily (Netflix-inspired)
class CircuitBreaker {
constructor(failureLimit = 3, resetTime = 5000) {
this.failures = 0;
this.state = "CLOSED";
this.failureLimit = failureLimit;
this.resetTime = resetTime;
}
async call(fn) {
if (this.state === "OPEN") {
throw new Error("Circuit is open");
}
try {
const result = await fn();
this.failures = 0;
return result;
} catch (err) {
this.failures++;
if (this.failures >= this.failureLimit) {
this.state
...Sign up for free to track which lessons you've completed and get learning reminders.