JavaScript Async/Await Error Handling Best Practices Debugging Utilities
Ready-to-use JavaScript utilities for async/await error handling best practices debugging with practical code snippets and debugging tools.
JavaScript Async/Await Error Handling Best Practices Debugging Utilities
These practical JavaScript async/await error handling best practices debugging utilities provide ready-to-use code for robust error handling, debugging, and monitoring in your applications.
Core Error Handling Utilities #
1. Async Wrapper with Error Context #
A utility that wraps async operations with comprehensive error handling and context preservation.
2. Retry Mechanism with Exponential Backoff #
A debugging-friendly retry utility that helps handle transient failures in async operations.
3. Async Operation Monitor #
A utility for monitoring and debugging multiple async operations with detailed logging.
// AsyncMonitor - Track multiple operations
class AsyncMonitor {
constructor() {
this.operations = new Map();
this.stats = { total: 0, successful: 0, failed: 0 };
}
async track(name, operation, timeout = 10000) {
const operationId = `${name}_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
const startTime = Date.now();
this.operations.set(operationId, {
name,
status: 'running',
startTime,
timeout
});
this.stats.total++;
try {
// Race between operation and timeout
const result = await Promise.race([
operation(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Timeout after ${timeout}ms`)), timeout)
)
]);
const duration = Date.now() - startTime;
this.operations.set(operationId, {
...this.operations.get(operationId),
status: 'completed',
duration,
result
});
this.stats.successful++;
return result;
} catch (error) {
const duration = Date.now() - startTime;
this.operations.set(operationId, {
...this.operations.get(operationId),
status: 'failed',
duration,
error: error.message
});
this.stats.failed++;
throw error;
}
}
getStats() {
return {
...this.stats,
successRate: ((this.stats.successful / this.stats.total) * 100).toFixed(1),
operations: Array.from(this.operations.values())
};
}
getRunningOperations() {
return Array.from(this.operations.values())
.filter(op => op.status === 'running');
}
}
4. Promise Timeout Wrapper #
A utility that adds timeout capabilities to any promise-based operation.
// promiseTimeout - Add timeout to any promise
function promiseTimeout(promise, timeoutMs, errorMessage) {
return new Promise((resolve, reject) => {
// Create timeout
const timeoutId = setTimeout(() => {
reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));
}, timeoutMs);
// Handle original promise
promise
.then(result => {
clearTimeout(timeoutId);
resolve(result);
})
.catch(error => {
clearTimeout(timeoutId);
reject(error);
});
});
}
// Usage example
async function slowOperation() {
await new Promise(resolve => setTimeout(resolve, 3000));
return "Completed!";
}
// With timeout
try {
const result = await promiseTimeout(
slowOperation(),
2000,
"Slow operation exceeded 2 second limit"
);
console.log(result);
} catch (error) {
console.error("Timeout error:", error.message);
}
5. Error Classification Utility #
Classify and handle different types of async errors appropriately.
// ErrorClassifier - Categorize errors for better handling
class ErrorClassifier {
static classify(error) {
const classification = {
type: 'unknown',
isRetryable: false,
severity: 'medium',
action: 'log'
};
// Network errors
if (error.message.includes('fetch') || error.message.includes('network')) {
classification.type = 'network';
classification.isRetryable = true;
classification.action = 'retry';
}
// Timeout errors
if (error.message.includes('timeout') || error.name === 'AbortError') {
classification.type = 'timeout';
classification.isRetryable = true;
classification.severity = 'high';
classification.action = 'retry_with_longer_timeout';
}
// Validation errors
if (error.message.includes('validation') || error.message.includes('invalid')) {
classification.type = 'validation';
classification.isRetryable = false;
classification.severity = 'low';
classification.action = 'fix_input';
}
// Server errors (5xx)
if (/5\d{2}/.test(error.message)) {
classification.type = 'server_error';
classification.isRetryable = true;
classification.severity = 'high';
classification.action = 'retry_with_backoff';
}
// Client errors (4xx)
if (/4\d{2}/.test(error.message)) {
classification.type = 'client_error';
classification.isRetryable = false;
classification.severity = 'medium';
classification.action = 'check_request';
}
return classification;
}
static async handleError(error, context = {}) {
const classification = this.classify(error);
console.error('Error classification:', {
...classification,
originalError: error.message,
context,
timestamp: new Date().toISOString()
});
// Return handling recommendation
return {
shouldRetry: classification.isRetryable,
action: classification.action,
severity: classification.severity,
type: classification.type
};
}
}
Debugging Helpers #
Development-Only Async Logger #
// AsyncLogger - Development debugging helper
class AsyncLogger {
static enabled = process.env.NODE_ENV === 'development';
static async trace(name, asyncOperation) {
if (!this.enabled) return asyncOperation;
console.group(`🔍 Async Trace: ${name}`);
console.time(name);
try {
const result = await asyncOperation;
console.log('✅ Result:', result);
return result;
} catch (error) {
console.error('❌ Error:', error);
throw error;
} finally {
console.timeEnd(name);
console.groupEnd();
}
}
}
// Usage
const result = await AsyncLogger.trace('fetchUserData',
fetch('/api/user').then(r => r.json())
);
Async Stack Trace Enhancer #
// enhanceAsyncStack - Better error stack traces
function enhanceAsyncStack(asyncFn, context) {
return async function(...args) {
try {
return await asyncFn.apply(this, args);
} catch (error) {
// Enhance stack trace with context
error.message = `${error.message}\n\nAsync Context: ${JSON.stringify(context, null, 2)}`;
throw error;
}
};
}
// Usage
const enhancedFetch = enhanceAsyncStack(
fetch,
{ operation: 'userDataFetch', timestamp: Date.now() }
);
Summary #
These JavaScript async/await error handling best practices debugging utilities provide:
- Comprehensive error wrapping with context preservation
- Intelligent retry mechanisms with exponential backoff
- Operation monitoring for debugging complex async flows
- Timeout handling to prevent hanging operations
- Error classification for appropriate handling strategies
- Development debugging tools for enhanced visibility
Copy and adapt these utilities to improve your async/await error handling and debugging capabilities.