8 Essential JavaScript Error‑Handling Techniques for More Resilient Apps
Master robust JavaScript error handling with eight practical strategies—including async try‑catch, global handlers, custom error types, graceful degradation, error boundaries, promise chain management, log level structuring, and automatic recovery—to boost application stability and improve user experience.
Error handling is often overlooked, yet it determines an application's robustness and user experience. Here are eight practical JavaScript error‑handling techniques to help build more reliable applications.
1. Wrap asynchronous code with try‑catch
Many developers think try‑catch only works for synchronous code, but it can also gracefully handle asynchronous operations.
// ❌ Bad example
async function fetchUserData() {
const response = await fetch('/api/users');
const data = await response.json();
return data;
}
// ✅ Good example
async function fetchUserData() {
try {
const response = await fetch('/api/users');
const data = await response.json();
return data;
} catch (error) {
// Distinguish error types
if (error instanceof TypeError) {
console.error('Network request failed:', error.message);
// Optionally retry or return cached data
} else {
console.error('Data parsing failed:', error.message);
}
// Return default value or throw custom error
return [];
}
}2. Implement a global error handler
A global error handler catches errors not captured by local try‑catch blocks, serving as the final defense for reliable applications.
// Handle regular JavaScript errors
window.onerror = function(message, source, lineno, colno, error) {
console.error({message, source, lineno, colno, error: error?.stack});
// Send report to monitoring service
sendErrorReport({
type: 'js_error',
details: {message, source, lineno, colno}
});
return true; // Prevent further propagation
};
// Handle unhandled Promise rejections
window.addEventListener('unhandledrejection', function(event) {
console.error('Unhandled Promise rejection:', event.reason);
event.preventDefault(); // Prevent default handling
});3. Create custom error types
Defining custom error classes helps differentiate and handle various error categories more effectively.
4. Graceful error degradation
When errors occur, provide sensible fallback solutions instead of letting the application crash.
5. Error boundary handling
In React, error boundaries prevent the whole app from crashing due to a local error; similar isolation can be implemented in plain JavaScript.
6. Optimize asynchronous error propagation chains
Properly handling error propagation in Promise chains is crucial.
7. Log level handling
Establishing a sensible log level system improves issue tracking and localization.
8. Error recovery mechanisms
Implement automatic recovery so the application can self‑heal after encountering errors.
class ServiceManager {
constructor(services) {
this.services = new Map(services);
this.healthChecks = new Map();
this.startMonitoring();
}
async startService(name) {
try {
const service = this.services.get(name);
await service.start();
this.monitorService(name);
} catch (error) {
console.error(`Service ${name} failed to start:`, error);
this.attemptRecovery(name);
}
}
monitorService(name) {
const healthCheck = setInterval(async () => {
try {
const service = this.services.get(name);
const isHealthy = await service.checkHealth();
if (!isHealthy) {
throw new Error('Service health check failed');
}
} catch (error) {
this.handleServiceFailure(name, error);
}
}, 30000);
this.healthChecks.set(name, healthCheck);
}
async handleServiceFailure(name, error) {
console.error(`Service ${name} error:`, error);
clearInterval(this.healthChecks.get(name));
this.healthChecks.delete(name);
await this.attemptRecovery(name);
}
async attemptRecovery(name, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
console.log(`Attempting recovery for ${name}, attempt ${i + 1}`);
await this.startService(name);
console.log(`Service ${name} recovered`);
return true;
} catch (error) {
console.error(`Recovery attempt ${i + 1} failed:`, error);
await new Promise(resolve => setTimeout(resolve, 5000 * (i + 1)));
}
}
console.error(`Service ${name} recovery failed after maximum retries`);
return false;
}
}Good error handling goes beyond merely catching errors; it involves gracefully managing them to keep the application running, and it should be an ongoing process that evolves with the app.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaScript
Provides JavaScript enthusiasts with tutorials and experience sharing on web front‑end technologies, including JavaScript, Node.js, Deno, Vue.js, React, Angular, HTML5, CSS3, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
