JsGuide

Learn JavaScript with practical tutorials and code examples

Code Snippet Intermediate
• Updated Aug 3, 2024

JavaScript Async/Await Error Prevention Utilities and Code Snippets

Ready-to-use utility functions and code snippets to prevent 'undefined is not a function' errors in JavaScript async/await operations.

JavaScript Async/Await Error Prevention Utilities

This collection of utility functions and code snippets helps prevent the common "undefined is not a function" error when working with JavaScript async/await patterns. Copy and use these utilities in your projects for more robust async code.

Core Utility Functions #

1. Safe Async Function Caller #

/**
 * Safely calls an async function with error handling
 * @param {Function} asyncFn - The async function to call
 * @param {Array} args - Arguments to pass to the function
 * @param {*} fallback - Fallback value if function fails
 * @returns {Promise} Result or fallback value
 */
async function safeAsyncCall(asyncFn, args = [], fallback = null) {
  try {
    if (typeof asyncFn !== 'function') {
      console.warn('safeAsyncCall: Expected a function, got:', typeof asyncFn);
      return fallback;
    }
    
    return await asyncFn(...args);
  } catch (error) {
    console.error('safeAsyncCall failed:', error.message);
    return fallback;
  }
}

// Example usage
const testFunction = async (name) => `Hello, ${name}!`;

// Test with valid function
safeAsyncCall(testFunction, ['World']).then(console.log); // "Hello, World!"

// Test with invalid function
safeAsyncCall(undefined, ['World'], 'Default').then(console.log); // "Default"

2. Async Method Validator #

/**
 * Validates and safely calls object methods in async context
 * @param {Object} obj - The object containing the method
 * @param {string} methodName - Name of the method to call
 * @param {Array} args - Arguments for the method
 * @param {*} fallback - Fallback value
 * @returns {Promise} Method result or fallback
 */
async function safeMethodCall(obj, methodName, args = [], fallback = null) {
  if (!obj || typeof obj !== 'object') {
    console.warn('safeMethodCall: Invalid object provided');
    return fallback;
  }
  
  if (!(methodName in obj)) {
    console.warn(`safeMethodCall: Method '${methodName}' not found in object`);
    return fallback;
  }
  
  if (typeof obj[methodName] !== 'function') {
    console.warn(`safeMethodCall: '${methodName}' is not a function`);
    return fallback;
  }
  
  try {
    const result = obj[methodName](...args);
    // Handle both sync and async methods
    return result instanceof Promise ? await result : result;
  } catch (error) {
    console.error(`safeMethodCall: Error calling '${methodName}':`, error.message);
    return fallback;
  }
}

// Example usage
const userAPI = {
  fetchUser: async (id) => ({ id, name: `User ${id}` }),
  validateUser: (user) => user && user.id && user.name
};

async function demo() {
  // Safe async method call
  const user = await safeMethodCall(userAPI, 'fetchUser', [123], { error: 'User not found' });
  console.log('User:', user);
  
  // Safe sync method call
  const isValid = await safeMethodCall(userAPI, 'validateUser', [user], false);
  console.log('Is valid:', isValid);
  
  // Safe call to non-existent method
  const result = await safeMethodCall(userAPI, 'nonExistent', [], 'No method');
  console.log('Non-existent result:', result);
}

demo();

3. Async Pipeline Utility #

/**
 * Creates a safe async pipeline that prevents function errors
 * @param {...Function} functions - Async functions to chain
 * @returns {Function} Pipeline function
 */
function createAsyncPipeline(...functions) {
  return async function(initialValue) {
    let result = initialValue;
    
    for (let i = 0; i < functions.length; i++) {
      const fn = functions[i];
      
      if (typeof fn !== 'function') {
        console.warn(`Pipeline step ${i}: Expected function, got ${typeof fn}`);
        continue;
      }
      
      try {
        const stepResult = await fn(result);
        result = stepResult !== undefined ? stepResult : result;
      } catch (error) {
        console.error(`Pipeline step ${i} failed:`, error.message);
        // Continue with previous result
      }
    }
    
    return result;
  };
}

// Example usage
const step1 = async (data) => ({ ...data, step1: true });
const step2 = async (data) => ({ ...data, step2: true });
const step3 = async (data) => ({ ...data, step3: true });

const pipeline = createAsyncPipeline(step1, step2, step3);

pipeline({ initial: 'data' }).then(result => {
  console.log('Pipeline result:', result);
});

4. Async Retry Utility #

/**
 * Retries an async function with exponential backoff
 * @param {Function} asyncFn - Function to retry
 * @param {Object} options - Retry configuration
 * @returns {Promise} Function result or error
 */
async function retryAsync(asyncFn, options = {}) {
  const {
    maxRetries = 3,
    baseDelay = 1000,
    maxDelay = 10000,
    backoffFactor = 2,
    onRetry = null
  } = options;
  
  if (typeof asyncFn !== 'function') {
    throw new Error('retryAsync: First argument must be a function');
  }
  
  let lastError;
  
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await asyncFn();
    } catch (error) {
      lastError = error;
      
      if (attempt === maxRetries) {
        break;
      }
      
      const delay = Math.min(
        baseDelay * Math.pow(backoffFactor, attempt),
        maxDelay
      );
      
      if (onRetry) {
        onRetry(error, attempt + 1, delay);
      }
      
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw lastError;
}

// Example usage
const unreliableFunction = async () => {
  if (Math.random() < 0.7) {
    throw new Error('Random failure');
  }
  return { success: true, timestamp: Date.now() };
};

retryAsync(unreliableFunction, {
  maxRetries: 3,
  baseDelay: 500,
  onRetry: (error, attempt, delay) => {
    console.log(`Retry ${attempt} after ${delay}ms due to:`, error.message);
  }
}).then(result => {
  console.log('Success:', result);
}).catch(error => {
  console.log('Final failure:', error.message);
});

5. Async Timeout Wrapper #

/**
 * Wraps an async function with a timeout
 * @param {Function} asyncFn - Function to wrap
 * @param {number} timeoutMs - Timeout in milliseconds
 * @param {string} timeoutMessage - Custom timeout message
 * @returns {Promise} Function result or timeout error
 */
function withTimeout(asyncFn, timeoutMs = 5000, timeoutMessage = 'Operation timed out') {
  if (typeof asyncFn !== 'function') {
    return Promise.reject(new Error('withTimeout: Expected a function'));
  }
  
  return function(...args) {
    return Promise.race([
      asyncFn.apply(this, args),
      new Promise((_, reject) => {
        setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
      })
    ]);
  };
}

// Example usage
const slowFunction = async (delay) => {
  await new Promise(resolve => setTimeout(resolve, delay));
  return `Completed after ${delay}ms`;
};

const timedFunction = withTimeout(slowFunction, 2000, 'Function took too long');

// This will succeed
timedFunction(1000).then(console.log).catch(console.error);

// This will timeout
timedFunction(3000).then(console.log).catch(console.error);

Error-Safe Async Patterns #

6. Batch Async Processing #

/**
 * Processes items in batches with error isolation
 * @param {Array} items - Items to process
 * @param {Function} processor - Async processing function
 * @param {Object} options - Processing options
 * @returns {Promise<Array>} Results array
 */
async function batchProcess(items, processor, options = {}) {
  const {
    batchSize = 5,
    continueOnError = true,
    errorValue = null
  } = options;
  
  if (!Array.isArray(items)) {
    throw new Error('batchProcess: Items must be an array');
  }
  
  if (typeof processor !== 'function') {
    throw new Error('batchProcess: Processor must be a function');
  }
  
  const results = [];
  
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    
    const batchPromises = batch.map(async (item, index) => {
      try {
        return await processor(item, i + index);
      } catch (error) {
        console.error(`Batch processing error at index ${i + index}:`, error.message);
        return continueOnError ? errorValue : Promise.reject(error);
      }
    });
    
    const batchResults = await Promise.all(batchPromises);
    results.push(...batchResults);
  }
  
  return results;
}

// Example usage
const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const processItem = async (item) => {
  if (item === 5) throw new Error('Item 5 always fails');
  return item * 2;
};

batchProcess(items, processItem, {
  batchSize: 3,
  continueOnError: true,
  errorValue: -1
}).then(results => {
  console.log('Batch results:', results);
});

7. Async Queue Manager #

/**
 * Manages async operations in a queue with concurrency control
 */
class AsyncQueue {
  constructor(concurrency = 1) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }
  
  /**
   * Adds a task to the queue
   * @param {Function} asyncFn - Async function to execute
   * @param {Array} args - Arguments for the function
   * @returns {Promise} Task result
   */
  add(asyncFn, args = []) {
    return new Promise((resolve, reject) => {
      if (typeof asyncFn !== 'function') {
        reject(new Error('AsyncQueue: Task must be a function'));
        return;
      }
      
      this.queue.push({
        asyncFn,
        args,
        resolve,
        reject
      });
      
      this.process();
    });
  }
  
  async process() {
    if (this.running >= this.concurrency || this.queue.length === 0) {
      return;
    }
    
    this.running++;
    const { asyncFn, args, resolve, reject } = this.queue.shift();
    
    try {
      const result = await asyncFn(...args);
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      this.running--;
      this.process(); // Process next task
    }
  }
  
  /**
   * Get queue status
   * @returns {Object} Queue status information
   */
  getStatus() {
    return {
      running: this.running,
      queued: this.queue.length,
      concurrency: this.concurrency
    };
  }
}

// Example usage
const queue = new AsyncQueue(2); // Max 2 concurrent tasks

const createTask = (id, delay) => async () => {
  console.log(`Task ${id} started`);
  await new Promise(resolve => setTimeout(resolve, delay));
  console.log(`Task ${id} completed`);
  return `Result ${id}`;
};

// Add tasks to queue
Promise.all([
  queue.add(createTask(1, 1000)),
  queue.add(createTask(2, 1500)),
  queue.add(createTask(3, 800)),
  queue.add(createTask(4, 1200))
]).then(results => {
  console.log('All tasks completed:', results);
});

// Check queue status
setInterval(() => {
  const status = queue.getStatus();
  if (status.running > 0 || status.queued > 0) {
    console.log('Queue status:', status);
  }
}, 500);

Usage Guidelines #

Quick Integration Checklist #

  1. Choose the right utility for your use case
  2. Test with edge cases (null, undefined, non-functions)
  3. Configure appropriate fallbacks for your application
  4. Add logging to monitor utility usage
  5. Consider performance impact for high-frequency operations

Best Practices #

  • Always validate function types before calling
  • Provide meaningful fallback values
  • Log errors appropriately for debugging
  • Use TypeScript when possible for compile-time checks
  • Test utilities with various input types

These utilities provide a solid foundation for preventing "undefined is not a function" errors in async JavaScript code. Copy the snippets you need and customize them for your specific use cases.

Related Snippets

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
Snippet Intermediate

Java vs Bedrock Seeds Comparison Utilities

Ready-to-use JavaScript utilities to compare and understand Java and Bedrock seed differences for game development.

#javascript #utilities #gaming +2
View Code
Data Structures
Snippet Intermediate

Fix JavaScript Async Await Promise Pending Code Utilities

Ready-to-use JavaScript utility functions to fix async await functions that return Promise pending instead of data.

#javascript #async #await +3
View Code
Syntax
Snippet Intermediate

JavaScript Callback Error Prevention Code Snippets

Ready-to-use JavaScript utilities to prevent undefined is not a function errors in callbacks. Copy-paste solutions for safer code.

#javascript #snippets #callbacks +2
View Code
Text Processing