JsGuide

Learn JavaScript with practical tutorials and code examples

Code Snippet Intermediate
• Updated Jan 15, 2025

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.

Related Snippets

Snippet Intermediate

JavaScript Async Await Promise Chain Debugging Utilities

Ready-to-use JavaScript utilities and code snippets to fix async await promise chain not working properly solutions with debugging tools.

#javascript #async-await #utilities +2
View Code
Syntax
Snippet Intermediate

JavaScript Error Handling Code Snippets and Utilities

Ready-to-use JavaScript error handling utilities and code snippets for robust application development and debugging.

#javascript #error #debugging +2
View Code
Syntax
Snippet Intermediate

JavaScript Utilities to Fix Undefined Is Not a Function Error

Ready-to-use JavaScript utility functions to fix undefined is not a function error with comprehensive error handling and validation code snippets.

#javascript #utilities #error-handling +2
View Code
Syntax
Snippet Intermediate

JavaScript Callback Utilities: Fix Undefined Function Errors

How to fix undefined is not a function error in JavaScript callbacks with production-ready utility functions and helper snippets.

#javascript #callbacks #utilities +3
View Code
Syntax