JavaScriptmediumJavaScript

Retry Mechanism with Exponential Backoff

Retry failed operations with exponential backoff and jitter

01

The problem

Need to retry failed operations with intelligent backoff strategy

02

The solution

JavaScript
async function retryWithBackoff(
  operation,
  options = {}
) {
  const {
    maxRetries = 3,
    initialDelay = 1000,
    maxDelay = 30000,
    backoffFactor = 2,
    jitter = true,
    shouldRetry = (error) => true,
    onRetry = (error, attempt, delay) => {},
    timeout = 0
  } = options;
  
  let lastError;
  
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      if (timeout > 0) {
        // Add timeout to operation
        return await Promise.race([
          operation(),
          new Promise((_, reject) =>
            setTimeout(() => reject(new Error('Operation timeout')), timeout)
          )
        ]);
      }
      return await operation();
    } catch (error) {
      lastError = error;
      
      if (attempt === maxRetries || !shouldRetry(error)) {
        throw error;
      }
      
      // Calculate delay with exponential backoff
      const baseDelay = initialDelay * Math.pow(backoffFactor, attempt);
      
      // Add jitter (randomness)
      let delay = baseDelay;
      if (jitter) {
        const jitterAmount = baseDelay * 0.1; // 10% jitter
        delay += Math.random() * jitterAmount * 2 - jitterAmount;
      }
      
      // Cap at max delay
      delay = Math.min(delay, maxDelay);
      
      // Notify about retry
      onRetry(error, attempt + 1, Math.round(delay));
      
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw lastError;
}

// Circuit breaker pattern
class CircuitBreaker {
  constructor(options = {}) {
    this.failureThreshold = options.failureThreshold || 5;
    this.resetTimeout = options.resetTimeout || 60000;
    this.state = 'CLOSED';
    this.failureCount = 0;
    this.nextAttempt = 0;
    this.lastFailureTime = null;
  }
  
  async execute(operation) {
    if (this.state === 'OPEN') {
      if (Date.now() >= this.nextAttempt) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }
    
    try {
      const result = await operation();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
    this.lastFailureTime = null;
  }
  
  onFailure() {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    
    if (this.failureCount >= this.failureThreshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.resetTimeout;
      
      // Auto-reset after timeout
      setTimeout(() => {
        if (this.state === 'OPEN') {
          this.state = 'HALF_OPEN';
        }
      }, this.resetTimeout);
    }
  }
  
  getState() {
    return {
      state: this.state,
      failureCount: this.failureCount,
      lastFailureTime: this.lastFailureTime,
      nextAttempt: this.nextAttempt
    };
  }
}

03

Parameters

operationfunction

Async function to retry

optionsobject

Retry configuration options

04

Put it to work

Example
// Simple retry with exponential backoff
async function fetchWithRetry(url) {
  return retryWithBackoff(
    async () => {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      return response.json();
    },
    {
      maxRetries: 5,
      initialDelay: 1000,
      backoffFactor: 2,
      onRetry: (error, attempt, delay) => {
        console.log(`Attempt ${attempt} failed, retrying in ${delay}ms: ${error.message}`);
      },
      shouldRetry: (error) => {
        // Don't retry 404 errors
        return !error.message.includes('404');
      }
    }
  );
}

// Circuit breaker example
const breaker = new CircuitBreaker({
  failureThreshold: 3,
  resetTimeout: 30000
});

async function callProtectedApi() {
  return breaker.execute(async () => {
    const response = await fetch('https://api.example.com/data');
    return response.json();
  });
}

// Monitor circuit state
setInterval(() => {
  console.log('Circuit state:', breaker.getState());
}, 5000);