+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 111 of 354

๐ŸŽญ Promise.allSettled: Handling Mixed Results in TypeScript

Master Promise.allSettled for resilient error handling with practical examples, batch processing, and fault-tolerant systems ๐Ÿ›ก๏ธ

๐Ÿš€Intermediate
22 min read

Prerequisites

  • Understanding of Promise fundamentals ๐Ÿ“
  • Experience with Promise.all and Promise.race โšก
  • Basic error handling patterns ๐Ÿ’ป

What you'll learn

  • Master Promise.allSettled for mixed success/failure scenarios ๐ŸŽฏ
  • Build resilient batch processing systems ๐Ÿ—๏ธ
  • Handle partial failures gracefully ๐Ÿ›
  • Create fault-tolerant applications with detailed reporting โœจ

๐ŸŽฏ Introduction

Welcome to the world of resilient Promise handling! ๐ŸŽญ Unlike Promise.all which fails fast, or Promise.race which only cares about the winner, Promise.allSettled() is like a patient teacher who waits for ALL students to finish their test - even if some fail!

Whether youโ€™re processing multiple API calls ๐ŸŒ, validating user data ๐Ÿ“‹, or running batch operations ๐Ÿ“ฆ, Promise.allSettled ensures you get complete results without letting a few failures ruin the entire operation.

By the end of this tutorial, youโ€™ll be a master of fault-tolerant async programming, able to handle mixed success and failure scenarios with grace and detailed insights. Letโ€™s dive into the world of โ€œno Promise left behindโ€! ๐Ÿš€

๐Ÿ“š Understanding Promise.allSettled()

๐Ÿค” What is Promise.allSettled()?

Think of Promise.allSettled() as a comprehensive survey ๐Ÿ“Š. Instead of stopping when the first person says โ€œnoโ€ (like Promise.all) or celebrating the first โ€œyesโ€ (like Promise.race), it waits patiently for EVERYONE to respond, then gives you a detailed breakdown of all responses.

// ๐ŸŽญ Basic Promise.allSettled example
const promises = [
  Promise.resolve("Success! ๐ŸŽ‰"),
  Promise.reject(new Error("Failed! ๐Ÿ’ฅ")),
  Promise.resolve("Another success! โœจ")
];

const results = await Promise.allSettled(promises);
console.log(results);
/*
[
  { status: 'fulfilled', value: 'Success! ๐ŸŽ‰' },
  { status: 'rejected', reason: Error: Failed! ๐Ÿ’ฅ },
  { status: 'fulfilled', value: 'Another success! โœจ' }
]
*/

๐Ÿ’ก Key Characteristics

  • ๐Ÿ›ก๏ธ Never Rejects: Always resolves, even if all promises fail
  • ๐Ÿ“Š Complete Results: Returns both successes and failures
  • ๐ŸŽฏ Type Safety: TypeScript provides excellent inference for result types
  • โณ Waits for All: Doesnโ€™t return until every promise settles
// ๐ŸŽจ TypeScript's type inference with Promise.allSettled
const mixed = Promise.allSettled([
  Promise.resolve("string"),           // PromiseSettledResult<string>
  Promise.resolve(42),                 // PromiseSettledResult<number>
  Promise.reject(new Error("boom"))    // PromiseSettledResult<never>
]);

// Type: Promise<PromiseSettledResult<string | number>[]>

๐Ÿ†š Comparison with Other Promise Methods

// ๐Ÿ Promise.all - Fast failure (one fails, all fail)
try {
  const allResults = await Promise.all([goodPromise, badPromise, goodPromise]);
  console.log("All succeeded:", allResults); // Won't reach here if badPromise fails
} catch (error) {
  console.log("One failed, stopping everything! ๐Ÿ’ฅ"); // Reaches here immediately
}

// ๐Ÿƒ Promise.race - First one wins (ignore the rest)
const raceWinner = await Promise.race([slowPromise, fastPromise, mediumPromise]);
console.log("First to finish:", raceWinner); // Only one result

// ๐ŸŽญ Promise.allSettled - Everyone gets heard
const settledResults = await Promise.allSettled([goodPromise, badPromise, goodPromise]);
console.log("All responses received:", settledResults); // Always succeeds with complete info

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Working with PromiseSettledResult

The magic is in the PromiseSettledResult type:

// ๐ŸŽฏ Understanding the result structure
interface PromiseFulfilledResult<T> {
  status: 'fulfilled';
  value: T;
}

interface PromiseRejectedResult {
  status: 'rejected';
  reason: any;
}

type PromiseSettledResult<T> = PromiseFulfilledResult<T> | PromiseRejectedResult;

// ๐Ÿ› ๏ธ Processing results safely
async function processSettledResults() {
  const promises = [
    Promise.resolve({ id: 1, name: "Alice ๐Ÿ‘ฉ" }),
    Promise.reject(new Error("Network timeout ๐Ÿ“ก")),
    Promise.resolve({ id: 2, name: "Bob ๐Ÿ‘จ" }),
    Promise.reject(new Error("Invalid data ๐Ÿ“"))
  ];
  
  const results = await Promise.allSettled(promises);
  
  // ๐ŸŽจ Process each result based on its status
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`โœ… Promise ${index + 1} succeeded:`, result.value);
    } else {
      console.log(`โŒ Promise ${index + 1} failed:`, result.reason.message);
    }
  });
  
  // ๐Ÿ“Š Get summary statistics
  const successful = results.filter(r => r.status === 'fulfilled');
  const failed = results.filter(r => r.status === 'rejected');
  
  console.log(`๐Ÿ“ˆ Summary: ${successful.length} succeeded, ${failed.length} failed`);
}

๐ŸŽฏ Helper Functions for Processing Results

// ๐Ÿ› ๏ธ Utility functions for common patterns
class SettledResultsHelper {
  
  // โœ… Extract only successful results
  static getSuccessful<T>(results: PromiseSettledResult<T>[]): T[] {
    return results
      .filter((result): result is PromiseFulfilledResult<T> => 
        result.status === 'fulfilled'
      )
      .map(result => result.value);
  }
  
  // โŒ Extract only failed results
  static getFailed<T>(results: PromiseSettledResult<T>[]): any[] {
    return results
      .filter((result): result is PromiseRejectedResult => 
        result.status === 'rejected'
      )
      .map(result => result.reason);
  }
  
  // ๐Ÿ“Š Get summary statistics
  static getSummary<T>(results: PromiseSettledResult<T>[]) {
    const successful = this.getSuccessful(results);
    const failed = this.getFailed(results);
    
    return {
      total: results.length,
      successful: successful.length,
      failed: failed.length,
      successRate: (successful.length / results.length) * 100,
      successfulResults: successful,
      failedReasons: failed
    };
  }
  
  // ๐ŸŽฏ Partition results into success/failure groups
  static partition<T>(results: PromiseSettledResult<T>[]) {
    const successes: T[] = [];
    const failures: any[] = [];
    
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        successes.push(result.value);
      } else {
        failures.push(result.reason);
      }
    });
    
    return { successes, failures };
  }
}

// ๐ŸŽฎ Using the helper functions
async function demonstrateHelpers() {
  const promises = [
    Promise.resolve("Data A ๐Ÿ“Š"),
    Promise.reject(new Error("Server down ๐Ÿ”ฅ")),
    Promise.resolve("Data B ๐Ÿ“ˆ"),
    Promise.reject(new Error("Timeout โฐ")),
    Promise.resolve("Data C ๐Ÿ“‰")
  ];
  
  const results = await Promise.allSettled(promises);
  const summary = SettledResultsHelper.getSummary(results);
  
  console.log("๐Ÿ“Š Processing Summary:");
  console.log(`  โœ… Successful: ${summary.successful}/${summary.total} (${summary.successRate.toFixed(1)}%)`);
  console.log(`  ๐Ÿ“ฆ Results: ${summary.successfulResults.join(', ')}`);
  console.log(`  โŒ Failures: ${summary.failedReasons.map(e => e.message).join(', ')}`);
}

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce Product Validation System

Letโ€™s build a robust product validation system that checks multiple criteria:

// ๐Ÿช Product interfaces
interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
  emoji: string;
}

interface ValidationResult {
  field: string;
  isValid: boolean;
  message: string;
  emoji: string;
}

interface ProductValidationReport {
  product: Product;
  validations: ValidationResult[];
  isOverallValid: boolean;
  validationSummary: string;
}

class ProductValidator {
  
  // ๐Ÿ” Validate product name
  private async validateName(product: Product): Promise<ValidationResult> {
    // ๐ŸŒ Simulate API call to check name uniqueness
    await this.delay(Math.random() * 300 + 100);
    
    const isValid = product.name.length >= 3 && product.name.length <= 50;
    
    if (!isValid) {
      throw new Error("Product name must be 3-50 characters long");
    }
    
    return {
      field: "name",
      isValid: true,
      message: `Name "${product.name}" is valid and unique โœจ`,
      emoji: "โœ…"
    };
  }
  
  // ๐Ÿ’ฐ Validate product price
  private async validatePrice(product: Product): Promise<ValidationResult> {
    await this.delay(Math.random() * 200 + 50);
    
    const isValid = product.price > 0 && product.price < 10000;
    
    if (!isValid) {
      throw new Error("Price must be between $0.01 and $9,999.99");
    }
    
    return {
      field: "price",
      isValid: true,
      message: `Price $${product.price.toFixed(2)} is within acceptable range ๐Ÿ’ฐ`,
      emoji: "โœ…"
    };
  }
  
  // ๐Ÿท๏ธ Validate product category
  private async validateCategory(product: Product): Promise<ValidationResult> {
    await this.delay(Math.random() * 400 + 150);
    
    const validCategories = ["electronics", "clothing", "books", "home", "sports"];
    const isValid = validCategories.includes(product.category.toLowerCase());
    
    if (!isValid) {
      throw new Error(`Category "${product.category}" is not supported. Valid: ${validCategories.join(', ')}`);
    }
    
    return {
      field: "category",
      isValid: true,
      message: `Category "${product.category}" is supported ๐Ÿท๏ธ`,
      emoji: "โœ…"
    };
  }
  
  // ๐Ÿ“ฆ Validate inventory availability
  private async validateInventory(product: Product): Promise<ValidationResult> {
    await this.delay(Math.random() * 500 + 200);
    
    // ๐ŸŽฒ Simulate 80% success rate for inventory check
    const isAvailable = Math.random() > 0.2;
    
    if (!isAvailable) {
      throw new Error("Product is currently out of stock");
    }
    
    return {
      field: "inventory",
      isValid: true,
      message: `Product is in stock and ready to ship ๐Ÿ“ฆ`,
      emoji: "โœ…"
    };
  }
  
  // ๐Ÿ›ก๏ธ Validate compliance (safety, regulations, etc.)
  private async validateCompliance(product: Product): Promise<ValidationResult> {
    await this.delay(Math.random() * 600 + 300);
    
    // ๐ŸŽฒ Simulate 85% success rate for compliance
    const isCompliant = Math.random() > 0.15;
    
    if (!isCompliant) {
      throw new Error("Product does not meet regulatory compliance standards");
    }
    
    return {
      field: "compliance",
      isValid: true,
      message: `Product meets all regulatory requirements ๐Ÿ›ก๏ธ`,
      emoji: "โœ…"
    };
  }
  
  // ๐ŸŽฏ Comprehensive product validation
  async validateProduct(product: Product): Promise<ProductValidationReport> {
    console.log(`๐Ÿ” Starting validation for ${product.emoji} ${product.name}...`);
    
    const validationPromises = [
      this.validateName(product),
      this.validatePrice(product),
      this.validateCategory(product),
      this.validateInventory(product),
      this.validateCompliance(product)
    ];
    
    // ๐ŸŽญ Use allSettled to get all validation results
    const settledResults = await Promise.allSettled(validationPromises);
    
    // ๐Ÿ“Š Process results
    const validations: ValidationResult[] = [];
    let passedCount = 0;
    
    settledResults.forEach((result, index) => {
      const fieldNames = ["name", "price", "category", "inventory", "compliance"];
      const fieldName = fieldNames[index];
      
      if (result.status === 'fulfilled') {
        validations.push(result.value);
        passedCount++;
      } else {
        validations.push({
          field: fieldName,
          isValid: false,
          message: result.reason.message,
          emoji: "โŒ"
        });
      }
    });
    
    const isOverallValid = passedCount === validationPromises.length;
    const validationSummary = `${passedCount}/${validationPromises.length} validations passed`;
    
    console.log(`๐Ÿ“‹ Validation complete: ${validationSummary}`);
    
    return {
      product,
      validations,
      isOverallValid,
      validationSummary
    };
  }
  
  // ๐Ÿ“Š Batch validate multiple products
  async validateProducts(products: Product[]): Promise<ProductValidationReport[]> {
    console.log(`๐Ÿš€ Starting batch validation for ${products.length} products...\n`);
    
    const validationPromises = products.map(product => this.validateProduct(product));
    
    // ๐ŸŽญ All products get validated regardless of individual failures
    const results = await Promise.allSettled(validationPromises);
    
    const reports: ProductValidationReport[] = [];
    let successfulValidations = 0;
    
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        reports.push(result.value);
        if (result.value.isOverallValid) {
          successfulValidations++;
        }
      } else {
        // ๐Ÿšจ Validation process itself failed (shouldn't happen in this example)
        console.error(`๐Ÿ’ฅ Validation failed for product ${index + 1}:`, result.reason);
      }
    });
    
    console.log(`\n๐Ÿ“ˆ Batch validation summary:`);
    console.log(`  ๐Ÿ“ฆ Products processed: ${products.length}`);
    console.log(`  โœ… Fully valid products: ${successfulValidations}`);
    console.log(`  โš ๏ธ Products with issues: ${products.length - successfulValidations}`);
    
    return reports;
  }
  
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// ๐ŸŽฎ Demo the product validation system
async function demonstrateProductValidation() {
  const validator = new ProductValidator();
  
  const products: Product[] = [
    { id: "1", name: "TypeScript Guide", price: 29.99, category: "books", emoji: "๐Ÿ“˜" },
    { id: "2", name: "๐Ÿƒโ€โ™‚๏ธ", price: -5.00, category: "invalid", emoji: "๐Ÿ‘Ÿ" }, // Invalid: bad name & price
    { id: "3", name: "Wireless Headphones", price: 199.99, category: "electronics", emoji: "๐ŸŽง" },
    { id: "4", name: "Coffee Mug", price: 15.99, category: "home", emoji: "โ˜•" },
    { id: "5", name: "Gaming Laptop Premium Ultra", price: 2499.99, category: "electronics", emoji: "๐Ÿ’ป" }
  ];
  
  try {
    const reports = await validator.validateProducts(products);
    
    console.log(`\n๐Ÿ“Š Detailed Validation Reports:`);
    reports.forEach((report, index) => {
      console.log(`\n${index + 1}. ${report.product.emoji} ${report.product.name}`);
      console.log(`   Overall: ${report.isOverallValid ? 'โœ… VALID' : 'โŒ INVALID'} (${report.validationSummary})`);
      
      report.validations.forEach(validation => {
        console.log(`   ${validation.emoji} ${validation.field}: ${validation.message}`);
      });
    });
    
  } catch (error) {
    console.error("๐Ÿ’ฅ Batch validation failed:", error);
  }
}

๐ŸŒŸ Example 2: Multi-Service Health Dashboard

Letโ€™s create a comprehensive health monitoring system:

// ๐Ÿฅ Service monitoring interfaces
interface ServiceConfig {
  name: string;
  url: string;
  timeout: number;
  critical: boolean;
  emoji: string;
}

interface HealthCheckResult {
  service: ServiceConfig;
  status: 'healthy' | 'degraded' | 'down';
  responseTime: number;
  details: string;
  timestamp: Date;
  error?: string;
}

interface SystemHealthReport {
  timestamp: Date;
  overallStatus: 'healthy' | 'degraded' | 'critical';
  services: HealthCheckResult[];
  summary: {
    total: number;
    healthy: number;
    degraded: number;
    down: number;
    criticalIssues: number;
  };
}

class HealthMonitor {
  private services: ServiceConfig[] = [
    { name: "User API", url: "https://api.example.com/users", timeout: 2000, critical: true, emoji: "๐Ÿ‘ฅ" },
    { name: "Payment Gateway", url: "https://payments.example.com/health", timeout: 3000, critical: true, emoji: "๐Ÿ’ณ" },
    { name: "Product Catalog", url: "https://catalog.example.com/status", timeout: 1500, critical: false, emoji: "๐Ÿ“ฆ" },
    { name: "Analytics Service", url: "https://analytics.example.com/ping", timeout: 5000, critical: false, emoji: "๐Ÿ“Š" },
    { name: "Email Service", url: "https://email.example.com/status", timeout: 4000, critical: false, emoji: "๐Ÿ“ง" },
    { name: "File Storage", url: "https://files.example.com/health", timeout: 2500, critical: true, emoji: "๐Ÿ—„๏ธ" }
  ];
  
  // ๐Ÿฅ Check single service health
  private async checkServiceHealth(service: ServiceConfig): Promise<HealthCheckResult> {
    const startTime = Date.now();
    const timestamp = new Date();
    
    try {
      // ๐ŸŒ Simulate health check with realistic scenarios
      await new Promise((resolve, reject) => {
        const simulatedResponseTime = Math.random() * service.timeout;
        
        setTimeout(() => {
          // ๐ŸŽฒ Different failure scenarios based on service type
          const rand = Math.random();
          
          if (service.critical && rand < 0.05) {
            // 5% chance critical services fail
            reject(new Error("Critical service unavailable"));
          } else if (!service.critical && rand < 0.15) {
            // 15% chance non-critical services fail
            reject(new Error("Service temporarily unavailable"));
          } else if (rand < 0.25) {
            // 25% chance of degraded performance
            setTimeout(() => resolve(undefined), simulatedResponseTime * 1.5);
            return;
          } else {
            resolve(undefined);
          }
        }, simulatedResponseTime);
      });
      
      const responseTime = Date.now() - startTime;
      
      // ๐ŸŽฏ Determine status based on response time
      let status: 'healthy' | 'degraded' | 'down';
      let details: string;
      
      if (responseTime < service.timeout * 0.5) {
        status = 'healthy';
        details = `Response time: ${responseTime}ms (excellent)`;
      } else if (responseTime < service.timeout * 0.8) {
        status = 'healthy';
        details = `Response time: ${responseTime}ms (good)`;
      } else {
        status = 'degraded';
        details = `Response time: ${responseTime}ms (slow but functional)`;
      }
      
      return {
        service,
        status,
        responseTime,
        details,
        timestamp
      };
      
    } catch (error) {
      const responseTime = Date.now() - startTime;
      
      return {
        service,
        status: 'down',
        responseTime,
        details: `Service check failed after ${responseTime}ms`,
        timestamp,
        error: error.message
      };
    }
  }
  
  // ๐Ÿ“Š Comprehensive system health check
  async getSystemHealth(): Promise<SystemHealthReport> {
    console.log("๐Ÿ” Performing system-wide health check...");
    const timestamp = new Date();
    
    // ๐ŸŽญ Check all services simultaneously with allSettled
    const healthPromises = this.services.map(service => this.checkServiceHealth(service));
    const settledResults = await Promise.allSettled(healthPromises);
    
    // ๐Ÿ“‹ Process all results (both successful and failed checks)
    const services: HealthCheckResult[] = [];
    
    settledResults.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        services.push(result.value);
      } else {
        // ๐Ÿšจ Health check itself failed (shouldn't happen in normal operation)
        const service = this.services[index];
        services.push({
          service,
          status: 'down',
          responseTime: 0,
          details: 'Health check failed to execute',
          timestamp,
          error: result.reason.message
        });
      }
    });
    
    // ๐Ÿ“ˆ Calculate summary statistics
    const summary = {
      total: services.length,
      healthy: services.filter(s => s.status === 'healthy').length,
      degraded: services.filter(s => s.status === 'degraded').length,
      down: services.filter(s => s.status === 'down').length,
      criticalIssues: services.filter(s => s.service.critical && s.status === 'down').length
    };
    
    // ๐ŸŽฏ Determine overall system status
    let overallStatus: 'healthy' | 'degraded' | 'critical';
    
    if (summary.criticalIssues > 0) {
      overallStatus = 'critical';
    } else if (summary.down > 0 || summary.degraded > 2) {
      overallStatus = 'degraded';
    } else if (summary.degraded > 0) {
      overallStatus = 'degraded';
    } else {
      overallStatus = 'healthy';
    }
    
    return {
      timestamp,
      overallStatus,
      services,
      summary
    };
  }
  
  // ๐Ÿ“Š Display detailed health report
  displayHealthReport(report: SystemHealthReport): void {
    const statusEmoji = {
      healthy: "๐Ÿ’š",
      degraded: "๐Ÿ’›", 
      critical: "๐Ÿ”ด"
    };
    
    console.log(`\n${statusEmoji[report.overallStatus]} System Health Report - ${report.timestamp.toLocaleString()}`);
    console.log(`Overall Status: ${report.overallStatus.toUpperCase()}\n`);
    
    console.log("๐Ÿ“‹ Service Details:");
    report.services.forEach(service => {
      const emoji = service.status === 'healthy' ? '๐Ÿ’š' : 
                   service.status === 'degraded' ? '๐Ÿ’›' : '๐Ÿ”ด';
      const critical = service.service.critical ? ' [CRITICAL]' : '';
      
      console.log(`  ${emoji} ${service.service.emoji} ${service.service.name}${critical}`);
      console.log(`     Status: ${service.status.toUpperCase()}`);
      console.log(`     Details: ${service.details}`);
      if (service.error) {
        console.log(`     Error: ${service.error}`);
      }
    });
    
    console.log(`\n๐Ÿ“Š Summary:`);
    console.log(`  ๐Ÿ’š Healthy: ${report.summary.healthy}/${report.summary.total}`);
    console.log(`  ๐Ÿ’› Degraded: ${report.summary.degraded}/${report.summary.total}`);
    console.log(`  ๐Ÿ”ด Down: ${report.summary.down}/${report.summary.total}`);
    if (report.summary.criticalIssues > 0) {
      console.log(`  ๐Ÿšจ Critical Issues: ${report.summary.criticalIssues}`);
    }
  }
  
  // ๐Ÿ”„ Continuous monitoring
  async startMonitoring(intervalMs: number = 30000): Promise<void> {
    console.log(`๐Ÿ”„ Starting continuous health monitoring (every ${intervalMs/1000}s)...\n`);
    
    const monitor = async () => {
      try {
        const report = await this.getSystemHealth();
        this.displayHealthReport(report);
        
        // ๐Ÿšจ Alert on critical issues
        if (report.overallStatus === 'critical') {
          console.log("\n๐Ÿšจ ALERT: Critical system issues detected!");
          const criticalServices = report.services.filter(s => 
            s.service.critical && s.status === 'down'
          );
          criticalServices.forEach(service => {
            console.log(`   ๐Ÿ”ฅ ${service.service.name}: ${service.error || 'Service down'}`);
          });
        }
        
        console.log("\n" + "=".repeat(80) + "\n");
        
      } catch (error) {
        console.error("๐Ÿ’ฅ Health monitoring failed:", error);
      }
    };
    
    // ๐ŸŽฏ Initial check
    await monitor();
    
    // โฐ Schedule recurring checks
    setInterval(monitor, intervalMs);
  }
}

// ๐ŸŽฎ Demo the health monitoring system
async function demonstrateHealthMonitoring() {
  const monitor = new HealthMonitor();
  
  try {
    // ๐Ÿ“Š Single health check
    const report = await monitor.getSystemHealth();
    monitor.displayHealthReport(report);
    
    // ๐ŸŽฏ You can uncomment this for continuous monitoring
    // await monitor.startMonitoring(10000); // Check every 10 seconds
    
  } catch (error) {
    console.error("๐Ÿ’ฅ Health monitoring demo failed:", error);
  }
}

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Retry with Partial Success Tracking

Handle retries while tracking which operations have already succeeded:

// ๐Ÿ”„ Advanced retry system with partial success tracking
class SmartRetryProcessor<T> {
  
  async processWithRetries<T>(
    operations: (() => Promise<T>)[],
    maxRetries: number = 3,
    retryDelay: number = 1000
  ): Promise<{
    results: (T | null)[];
    successful: T[];
    failed: { index: number; error: any; attempts: number }[];
    summary: string;
  }> {
    
    const results: (T | null)[] = new Array(operations.length).fill(null);
    const successful: T[] = [];
    const failed: { index: number; error: any; attempts: number }[] = [];
    const pendingIndices = new Set(operations.map((_, i) => i));
    
    for (let attempt = 1; attempt <= maxRetries && pendingIndices.size > 0; attempt++) {
      console.log(`\n๐Ÿ”„ Attempt ${attempt}: Processing ${pendingIndices.size} operations...`);
      
      // ๐ŸŽฏ Only retry the operations that haven't succeeded yet
      const currentPromises = Array.from(pendingIndices).map(index => 
        operations[index]().then(
          result => ({ index, status: 'fulfilled' as const, value: result }),
          error => ({ index, status: 'rejected' as const, reason: error })
        )
      );
      
      // ๐ŸŽญ Wait for all current attempts to settle
      const settledResults = await Promise.allSettled(currentPromises);
      
      // ๐Ÿ“Š Process this round of results
      settledResults.forEach(settledResult => {
        if (settledResult.status === 'fulfilled') {
          const { index, status, value, reason } = settledResult.value;
          
          if (status === 'fulfilled') {
            // โœ… Operation succeeded
            results[index] = value;
            successful.push(value);
            pendingIndices.delete(index);
            console.log(`  โœ… Operation ${index + 1} succeeded on attempt ${attempt}`);
          } else {
            // โŒ Operation failed this round
            console.log(`  โŒ Operation ${index + 1} failed on attempt ${attempt}: ${reason.message}`);
            
            if (attempt === maxRetries) {
              // ๐Ÿšซ Final failure
              failed.push({ index, error: reason, attempts: attempt });
              pendingIndices.delete(index);
            }
          }
        }
      });
      
      // โฐ Delay before next retry (if needed)
      if (pendingIndices.size > 0 && attempt < maxRetries) {
        console.log(`โฐ Waiting ${retryDelay}ms before next retry...`);
        await new Promise(resolve => setTimeout(resolve, retryDelay));
      }
    }
    
    const summary = `${successful.length} succeeded, ${failed.length} failed after ${maxRetries} attempts`;
    console.log(`\n๐Ÿ“Š Final summary: ${summary}`);
    
    return { results, successful, failed, summary };
  }
}

// ๐ŸŽฎ Demo smart retry processor
async function demonstrateSmartRetry() {
  const processor = new SmartRetryProcessor();
  
  // ๐ŸŽฏ Create operations with different failure rates
  const operations = [
    () => new Promise<string>((resolve, reject) => {
      setTimeout(() => {
        Math.random() > 0.3 ? resolve("Operation A succeeded! ๐Ÿ…ฐ๏ธ") : reject(new Error("A failed"));
      }, 200);
    }),
    () => new Promise<string>((resolve, reject) => {
      setTimeout(() => {
        Math.random() > 0.7 ? resolve("Operation B succeeded! ๐Ÿ…ฑ๏ธ") : reject(new Error("B failed"));
      }, 300);
    }),
    () => new Promise<string>((resolve, reject) => {
      setTimeout(() => {
        Math.random() > 0.1 ? resolve("Operation C succeeded! ๐Ÿ†˜") : reject(new Error("C failed"));
      }, 150);
    }),
    () => new Promise<string>((resolve, reject) => {
      setTimeout(() => {
        Math.random() > 0.5 ? resolve("Operation D succeeded! ๐Ÿ”ค") : reject(new Error("D failed"));
      }, 400);
    })
  ];
  
  try {
    const result = await processor.processWithRetries(operations, 4, 500);
    
    console.log(`\n๐ŸŽ‰ Processing complete!`);
    console.log(`โœ… Successful operations: ${result.successful.length}`);
    result.successful.forEach(success => console.log(`  - ${success}`));
    
    if (result.failed.length > 0) {
      console.log(`โŒ Failed operations: ${result.failed.length}`);
      result.failed.forEach(failure => 
        console.log(`  - Operation ${failure.index + 1}: ${failure.error.message} (${failure.attempts} attempts)`)
      );
    }
    
  } catch (error) {
    console.error("๐Ÿ’ฅ Smart retry demo failed:", error);
  }
}

๐Ÿ—๏ธ Batch Processing with Progress Reporting

Process large batches with detailed progress tracking:

// ๐Ÿ“Š Advanced batch processor with progress tracking
class BatchProcessor<T, R> {
  
  async processBatch<T, R>(
    items: T[],
    processor: (item: T, index: number) => Promise<R>,
    options: {
      batchSize?: number;
      maxConcurrency?: number;
      progressCallback?: (progress: BatchProgress<T, R>) => void;
    } = {}
  ): Promise<BatchResult<T, R>> {
    
    const { batchSize = 10, maxConcurrency = 5, progressCallback } = options;
    const results: (R | null)[] = new Array(items.length).fill(null);
    const errors: { index: number; item: T; error: any }[] = [];
    let completed = 0;
    
    console.log(`๐Ÿš€ Starting batch processing: ${items.length} items, batches of ${batchSize}`);
    
    // ๐Ÿ“ฆ Split items into batches
    const batches: T[][] = [];
    for (let i = 0; i < items.length; i += batchSize) {
      batches.push(items.slice(i, i + batchSize));
    }
    
    // ๐Ÿ”„ Process each batch
    for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
      const batch = batches[batchIndex];
      const batchStartIndex = batchIndex * batchSize;
      
      console.log(`\n๐Ÿ“ฆ Processing batch ${batchIndex + 1}/${batches.length} (${batch.length} items)...`);
      
      // ๐ŸŽฏ Limit concurrency within the batch
      const promises = batch.map((item, itemIndex) => {
        const globalIndex = batchStartIndex + itemIndex;
        return processor(item, globalIndex).then(
          result => ({ index: globalIndex, success: true, result, item }),
          error => ({ index: globalIndex, success: false, error, item })
        );
      });
      
      // ๐ŸŽญ Wait for all items in this batch to complete
      const batchResults = await Promise.allSettled(promises);
      
      // ๐Ÿ“Š Process batch results
      batchResults.forEach(settledResult => {
        if (settledResult.status === 'fulfilled') {
          const { index, success, result, error, item } = settledResult.value;
          
          if (success) {
            results[index] = result;
          } else {
            errors.push({ index, item, error });
          }
          
          completed++;
          
          // ๐Ÿ“ˆ Report progress
          const progress: BatchProgress<T, R> = {
            total: items.length,
            completed,
            successful: completed - errors.length,
            failed: errors.length,
            percentage: (completed / items.length) * 100,
            currentBatch: batchIndex + 1,
            totalBatches: batches.length,
            results: results.filter(r => r !== null) as R[],
            errors
          };
          
          progressCallback?.(progress);
        }
      });
      
      console.log(`  โœ… Batch ${batchIndex + 1} complete: ${completed}/${items.length} total items processed`);
      
      // โฐ Small delay between batches to prevent overwhelming
      if (batchIndex < batches.length - 1) {
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    }
    
    const successful = results.filter(r => r !== null) as R[];
    const summary = `${successful.length} succeeded, ${errors.length} failed`;
    
    console.log(`\n๐ŸŽ‰ Batch processing complete! ${summary}`);
    
    return {
      items,
      results,
      successful,
      errors,
      summary,
      statistics: {
        totalItems: items.length,
        successfulItems: successful.length,
        failedItems: errors.length,
        successRate: (successful.length / items.length) * 100,
        totalBatches: batches.length,
        avgItemsPerBatch: items.length / batches.length
      }
    };
  }
}

interface BatchProgress<T, R> {
  total: number;
  completed: number;
  successful: number;
  failed: number;
  percentage: number;
  currentBatch: number;
  totalBatches: number;
  results: R[];
  errors: { index: number; item: T; error: any }[];
}

interface BatchResult<T, R> {
  items: T[];
  results: (R | null)[];
  successful: R[];
  errors: { index: number; item: T; error: any }[];
  summary: string;
  statistics: {
    totalItems: number;
    successfulItems: number;
    failedItems: number;
    successRate: number;
    totalBatches: number;
    avgItemsPerBatch: number;
  };
}

// ๐ŸŽฎ Demo batch processor
async function demonstrateBatchProcessing() {
  const processor = new BatchProcessor();
  
  // ๐Ÿ“Š Create sample data to process
  const users = Array.from({ length: 25 }, (_, i) => ({
    id: i + 1,
    name: `User ${i + 1}`,
    email: `user${i + 1}@example.com`,
    emoji: ['๐Ÿ‘ค', '๐Ÿ‘ฅ', '๐Ÿง‘', '๐Ÿ‘จ', '๐Ÿ‘ฉ'][i % 5]
  }));
  
  // ๐Ÿ”„ Define processing function (simulate API calls)
  const processUser = async (user: typeof users[0], index: number) => {
    // ๐ŸŒ Simulate variable processing times
    const delay = 200 + Math.random() * 800;
    await new Promise(resolve => setTimeout(resolve, delay));
    
    // ๐ŸŽฒ Simulate 15% failure rate
    if (Math.random() < 0.15) {
      throw new Error(`Failed to process ${user.name}`);
    }
    
    return {
      ...user,
      processed: true,
      processedAt: new Date(),
      processingTime: delay
    };
  };
  
  try {
    const result = await processor.processBatch(
      users,
      processUser,
      {
        batchSize: 5,
        maxConcurrency: 3,
        progressCallback: (progress) => {
          console.log(`    ๐Ÿ“ˆ Progress: ${progress.completed}/${progress.total} (${progress.percentage.toFixed(1)}%) - โœ…${progress.successful} โŒ${progress.failed}`);
        }
      }
    );
    
    console.log(`\n๐Ÿ“Š Final Results:`);
    console.log(`  ๐Ÿ“ฆ Total items: ${result.statistics.totalItems}`);
    console.log(`  โœ… Successful: ${result.statistics.successfulItems} (${result.statistics.successRate.toFixed(1)}%)`);
    console.log(`  โŒ Failed: ${result.statistics.failedItems}`);
    console.log(`  ๐Ÿ“‹ Total batches: ${result.statistics.totalBatches}`);
    
    if (result.errors.length > 0) {
      console.log(`\nโŒ Failed items:`);
      result.errors.forEach(error => {
        console.log(`  - ${error.item.emoji} ${error.item.name}: ${error.error.message}`);
      });
    }
    
  } catch (error) {
    console.error("๐Ÿ’ฅ Batch processing demo failed:", error);
  }
}

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Ignoring Partial Failures

// โŒ Dangerous - treating partial success as complete failure
async function badPartialHandling() {
  const promises = [
    fetch('/api/user/1'),
    fetch('/api/user/2'),  // This might fail
    fetch('/api/user/3')
  ];
  
  const results = await Promise.allSettled(promises);
  
  // ๐Ÿ’ฅ Wrong: Give up if any failed
  const hasFailures = results.some(r => r.status === 'rejected');
  if (hasFailures) {
    throw new Error("Some requests failed, aborting everything!");
  }
  
  // ๐Ÿšซ This logic loses valuable partial data
}

// โœ… Smart - work with partial success
async function smartPartialHandling() {
  const promises = [
    fetch('/api/user/1').then(r => r.json()),
    fetch('/api/user/2').then(r => r.json()),
    fetch('/api/user/3').then(r => r.json())
  ];
  
  const results = await Promise.allSettled(promises);
  
  // โœ… Extract successful results
  const users = results
    .filter((result): result is PromiseFulfilledResult<any> => 
      result.status === 'fulfilled'
    )
    .map(result => result.value);
  
  // โœ… Log failures for debugging but continue with partial data
  const failures = results
    .filter(result => result.status === 'rejected')
    .map(result => result.reason);
  
  if (failures.length > 0) {
    console.warn(`โš ๏ธ ${failures.length} requests failed, proceeding with ${users.length} users`);
  }
  
  return users; // โœ… Return what we could successfully get
}

๐Ÿคฏ Pitfall 2: Not Handling Processing Errors

// โŒ Wrong - assuming allSettled results are always processable
async function badResultProcessing() {
  const promises = [
    Promise.resolve('{"valid": "json"}'),
    Promise.resolve('invalid json'),
    Promise.resolve('{"another": "valid"}')
  ];
  
  const results = await Promise.allSettled(promises);
  
  // ๐Ÿ’ฅ This will crash on invalid JSON
  const parsed = results.map(result => {
    if (result.status === 'fulfilled') {
      return JSON.parse(result.value); // ๐Ÿ’ฅ Boom on invalid JSON!
    }
    return null;
  });
}

// โœ… Safe - handle processing errors too
async function safeResultProcessing() {
  const promises = [
    Promise.resolve('{"valid": "json"}'),
    Promise.resolve('invalid json'),
    Promise.resolve('{"another": "valid"}')
  ];
  
  const results = await Promise.allSettled(promises);
  
  // โœ… Safely process each result
  const processed = results.map((result, index) => {
    try {
      if (result.status === 'fulfilled') {
        const parsed = JSON.parse(result.value);
        return { success: true, data: parsed, index };
      } else {
        return { success: false, error: result.reason.message, index };
      }
    } catch (parseError) {
      return { success: false, error: `JSON parse error: ${parseError.message}`, index };
    }
  });
  
  const successful = processed.filter(p => p.success).map(p => p.data);
  const failed = processed.filter(p => !p.success);
  
  console.log(`โœ… Parsed ${successful.length} items, ${failed.length} failed`);
  return { successful, failed };
}

๐ŸŽฏ Pitfall 3: Memory Issues with Large Batches

// โŒ Memory intensive - processing huge arrays at once
async function memoryIntensiveProcessing(items: any[]) {
  // ๐Ÿ’ฅ This loads ALL results into memory simultaneously
  const promises = items.map(item => expensiveOperation(item));
  const results = await Promise.allSettled(promises);
  return results; // Potentially huge array in memory
}

// โœ… Memory efficient - process in chunks
async function memoryEfficientProcessing<T, R>(
  items: T[], 
  processor: (item: T) => Promise<R>,
  chunkSize: number = 100
): Promise<R[]> {
  const allResults: R[] = [];
  
  // ๐Ÿ“ฆ Process in manageable chunks
  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    console.log(`๐Ÿ“ฆ Processing chunk ${Math.floor(i/chunkSize) + 1}/${Math.ceil(items.length/chunkSize)}`);
    
    const chunkPromises = chunk.map(processor);
    const chunkResults = await Promise.allSettled(chunkPromises);
    
    // โœ… Extract successful results and free memory
    const successful = chunkResults
      .filter((r): r is PromiseFulfilledResult<R> => r.status === 'fulfilled')
      .map(r => r.value);
    
    allResults.push(...successful);
    
    // ๐Ÿงน Allow garbage collection between chunks
    if (i + chunkSize < items.length) {
      await new Promise(resolve => setTimeout(resolve, 10));
    }
  }
  
  return allResults;
}

async function expensiveOperation(item: any): Promise<any> {
  // Simulate expensive operation
  await new Promise(resolve => setTimeout(resolve, 100));
  return { processed: item, timestamp: Date.now() };
}

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Embrace Partial Success: Design systems that work with partial data rather than all-or-nothing
  2. ๐Ÿ“Š Always Analyze Results: Use helper functions to categorize and summarize settled results
  3. ๐Ÿ”„ Implement Smart Retries: Only retry failed operations, not successful ones
  4. ๐Ÿ“ˆ Provide Progress Feedback: Keep users informed during long-running batch operations
  5. ๐Ÿงน Manage Memory: Process large datasets in chunks to avoid memory issues
  6. ๐Ÿšจ Plan for Edge Cases: Handle scenarios where all operations fail or succeed
  7. ๐Ÿ“ Log Comprehensive Details: Capture both successes and failures for debugging and analytics

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Distributed File Processing System

Create a robust file processing system that handles multiple file sources with mixed success/failure:

๐Ÿ“‹ Requirements:

  • ๐Ÿ—‚๏ธ Process files from multiple sources (local, S3, FTP, HTTP)
  • โœจ Apply multiple transformations (resize, format, validate, encrypt)
  • ๐Ÿ“Š Generate detailed processing reports with partial success tracking
  • ๐Ÿ”„ Implement intelligent retry for failed operations only
  • ๐Ÿ’พ Save successful results even if some operations fail
  • ๐Ÿ“ˆ Provide real-time progress updates

๐Ÿš€ Bonus Points:

  • Add file type-specific processing rules
  • Implement rollback for partially processed files
  • Create a processing queue with priority levels
  • Add estimated time remaining calculations
  • Generate performance analytics and recommendations

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐Ÿ—‚๏ธ Distributed file processing system with Promise.allSettled
interface FileSource {
  id: string;
  type: 'local' | 's3' | 'ftp' | 'http';
  location: string;
  priority: number;
  emoji: string;
}

interface FileInfo {
  source: FileSource;
  filename: string;
  size: number;
  mimeType: string;
  checksum: string;
}

interface ProcessingStep {
  name: string;
  processor: (file: FileInfo) => Promise<ProcessedFile>;
  required: boolean;
  emoji: string;
}

interface ProcessedFile {
  original: FileInfo;
  result: any;
  processingTime: number;
  step: string;
}

interface ProcessingResult {
  file: FileInfo;
  steps: {
    step: string;
    success: boolean;
    result?: ProcessedFile;
    error?: string;
    duration: number;
  }[];
  overallSuccess: boolean;
  completedSteps: number;
  totalSteps: number;
}

interface BatchProcessingReport {
  totalFiles: number;
  fullyProcessed: number;
  partiallyProcessed: number;
  failed: number;
  processingTime: number;
  results: ProcessingResult[];
  summary: string;
}

class DistributedFileProcessor {
  private processingSteps: ProcessingStep[] = [
    {
      name: 'validate',
      processor: this.validateFile.bind(this),
      required: true,
      emoji: '๐Ÿ”'
    },
    {
      name: 'resize',
      processor: this.resizeFile.bind(this),
      required: false,
      emoji: '๐Ÿ“'
    },
    {
      name: 'format',
      processor: this.formatFile.bind(this),
      required: true,
      emoji: '๐ŸŽจ'
    },
    {
      name: 'encrypt',
      processor: this.encryptFile.bind(this),
      required: false,
      emoji: '๐Ÿ”’'
    },
    {
      name: 'upload',
      processor: this.uploadFile.bind(this),
      required: true,
      emoji: 'โ˜๏ธ'
    }
  ];
  
  // ๐Ÿ” File validation step
  private async validateFile(file: FileInfo): Promise<ProcessedFile> {
    const startTime = Date.now();
    await this.delay(100 + Math.random() * 200);
    
    // ๐ŸŽฒ 10% chance of validation failure
    if (Math.random() < 0.1) {
      throw new Error(`File validation failed: corrupted or invalid format`);
    }
    
    const processingTime = Date.now() - startTime;
    return {
      original: file,
      result: { validated: true, checks: ['format', 'size', 'integrity'] },
      processingTime,
      step: 'validate'
    };
  }
  
  // ๐Ÿ“ File resize step
  private async resizeFile(file: FileInfo): Promise<ProcessedFile> {
    const startTime = Date.now();
    await this.delay(300 + Math.random() * 500);
    
    // ๐ŸŽฒ 5% chance of resize failure
    if (Math.random() < 0.05) {
      throw new Error(`Resize failed: unsupported image format`);
    }
    
    const processingTime = Date.now() - startTime;
    return {
      original: file,
      result: { resized: true, dimensions: { width: 800, height: 600 } },
      processingTime,
      step: 'resize'
    };
  }
  
  // ๐ŸŽจ File format conversion step
  private async formatFile(file: FileInfo): Promise<ProcessedFile> {
    const startTime = Date.now();
    await this.delay(200 + Math.random() * 400);
    
    // ๐ŸŽฒ 8% chance of format failure
    if (Math.random() < 0.08) {
      throw new Error(`Format conversion failed: codec not available`);
    }
    
    const processingTime = Date.now() - startTime;
    return {
      original: file,
      result: { formatted: true, outputFormat: 'webp', compression: 85 },
      processingTime,
      step: 'format'
    };
  }
  
  // ๐Ÿ”’ File encryption step
  private async encryptFile(file: FileInfo): Promise<ProcessedFile> {
    const startTime = Date.now();
    await this.delay(150 + Math.random() * 300);
    
    // ๐ŸŽฒ 3% chance of encryption failure
    if (Math.random() < 0.03) {
      throw new Error(`Encryption failed: key generation error`);
    }
    
    const processingTime = Date.now() - startTime;
    return {
      original: file,
      result: { encrypted: true, algorithm: 'AES-256', keyId: 'key-' + Math.random().toString(36).substr(2, 9) },
      processingTime,
      step: 'encrypt'
    };
  }
  
  // โ˜๏ธ File upload step
  private async uploadFile(file: FileInfo): Promise<ProcessedFile> {
    const startTime = Date.now();
    await this.delay(400 + Math.random() * 600);
    
    // ๐ŸŽฒ 12% chance of upload failure
    if (Math.random() < 0.12) {
      throw new Error(`Upload failed: network timeout or storage full`);
    }
    
    const processingTime = Date.now() - startTime;
    return {
      original: file,
      result: { uploaded: true, url: `https://cdn.example.com/${file.filename}`, etag: 'etag-' + Math.random().toString(36).substr(2, 12) },
      processingTime,
      step: 'upload'
    };
  }
  
  // ๐ŸŽฏ Process single file through all steps
  async processFile(file: FileInfo): Promise<ProcessingResult> {
    console.log(`๐Ÿ“ Processing ${file.source.emoji} ${file.filename}...`);
    
    const stepPromises = this.processingSteps.map(async (step) => {
      const startTime = Date.now();
      
      try {
        const result = await step.processor(file);
        const duration = Date.now() - startTime;
        
        return {
          step: step.name,
          success: true,
          result,
          duration
        };
      } catch (error) {
        const duration = Date.now() - startTime;
        
        return {
          step: step.name,
          success: false,
          error: error.message,
          duration
        };
      }
    });
    
    // ๐ŸŽญ Wait for all processing steps to complete
    const settledResults = await Promise.allSettled(stepPromises);
    
    // ๐Ÿ“Š Process the results
    const steps = settledResults.map(result => 
      result.status === 'fulfilled' ? result.value : {
        step: 'unknown',
        success: false,
        error: 'Step processing failed',
        duration: 0
      }
    );
    
    const completedSteps = steps.filter(s => s.success).length;
    const requiredSteps = this.processingSteps.filter(s => s.required);
    const completedRequiredSteps = steps.filter(s => 
      s.success && requiredSteps.some(rs => rs.name === s.step)
    ).length;
    
    const overallSuccess = completedRequiredSteps === requiredSteps.length;
    
    console.log(`  ${overallSuccess ? 'โœ…' : 'โš ๏ธ'} ${file.filename}: ${completedSteps}/${this.processingSteps.length} steps completed`);
    
    return {
      file,
      steps,
      overallSuccess,
      completedSteps,
      totalSteps: this.processingSteps.length
    };
  }
  
  // ๐Ÿš€ Process multiple files with detailed reporting
  async processBatch(files: FileInfo[]): Promise<BatchProcessingReport> {
    console.log(`๐Ÿš€ Starting batch processing of ${files.length} files...\n`);
    const startTime = Date.now();
    
    // ๐ŸŽญ Process all files concurrently with allSettled
    const filePromises = files.map(file => this.processFile(file));
    const settledResults = await Promise.allSettled(filePromises);
    
    // ๐Ÿ“Š Extract successful processing results
    const results: ProcessingResult[] = settledResults
      .filter((result): result is PromiseFulfilledResult<ProcessingResult> => 
        result.status === 'fulfilled'
      )
      .map(result => result.value);
    
    // ๐Ÿ“ˆ Calculate statistics
    const fullyProcessed = results.filter(r => r.overallSuccess).length;
    const partiallyProcessed = results.filter(r => 
      !r.overallSuccess && r.completedSteps > 0
    ).length;
    const failed = results.filter(r => r.completedSteps === 0).length;
    const processingTime = Date.now() - startTime;
    
    const summary = `${fullyProcessed} fully processed, ${partiallyProcessed} partially processed, ${failed} failed`;
    
    console.log(`\n๐ŸŽ‰ Batch processing complete in ${processingTime}ms!`);
    console.log(`๐Ÿ“Š Results: ${summary}`);
    
    return {
      totalFiles: files.length,
      fullyProcessed,
      partiallyProcessed,
      failed,
      processingTime,
      results,
      summary
    };
  }
  
  // ๐Ÿ“Š Display detailed batch report
  displayDetailedReport(report: BatchProcessingReport): void {
    console.log(`\n๐Ÿ“‹ Detailed Processing Report`);
    console.log(`${'='.repeat(50)}`);
    
    console.log(`\n๐Ÿ“Š Overview:`);
    console.log(`  ๐Ÿ“ Total files: ${report.totalFiles}`);
    console.log(`  โœ… Fully processed: ${report.fullyProcessed}`);
    console.log(`  โš ๏ธ Partially processed: ${report.partiallyProcessed}`);
    console.log(`  โŒ Failed: ${report.failed}`);
    console.log(`  โฑ๏ธ Total time: ${report.processingTime}ms`);
    console.log(`  ๐Ÿ“ˆ Success rate: ${((report.fullyProcessed / report.totalFiles) * 100).toFixed(1)}%`);
    
    console.log(`\n๐Ÿ“ File Details:`);
    report.results.forEach((result, index) => {
      const status = result.overallSuccess ? 'โœ… SUCCESS' : 
                    result.completedSteps > 0 ? 'โš ๏ธ PARTIAL' : 'โŒ FAILED';
      
      console.log(`\n${index + 1}. ${result.file.source.emoji} ${result.file.filename}`);
      console.log(`   Status: ${status} (${result.completedSteps}/${result.totalSteps} steps)`);
      
      result.steps.forEach(step => {
        const stepInfo = this.processingSteps.find(s => s.name === step.step);
        const emoji = step.success ? 'โœ…' : 'โŒ';
        const required = stepInfo?.required ? ' [REQUIRED]' : '';
        
        console.log(`   ${emoji} ${stepInfo?.emoji} ${step.step}${required}: ${step.duration}ms`);
        if (!step.success && step.error) {
          console.log(`     Error: ${step.error}`);
        }
      });
    });
    
    // ๐Ÿ”ง Processing step analysis
    console.log(`\n๐Ÿ”ง Step Analysis:`);
    this.processingSteps.forEach(step => {
      const stepResults = report.results.flatMap(r => 
        r.steps.filter(s => s.step === step.name)
      );
      const successful = stepResults.filter(s => s.success).length;
      const total = stepResults.length;
      const avgTime = stepResults.reduce((sum, s) => sum + s.duration, 0) / total;
      
      console.log(`  ${step.emoji} ${step.name}: ${successful}/${total} (${((successful/total)*100).toFixed(1)}%) - avg ${avgTime.toFixed(0)}ms`);
    });
  }
  
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// ๐ŸŽฎ Demo the distributed file processor
async function demonstrateFileProcessing() {
  const processor = new DistributedFileProcessor();
  
  // ๐Ÿ“ Create sample files from different sources
  const files: FileInfo[] = [
    {
      source: { id: 's3-1', type: 's3', location: 'bucket-1', priority: 1, emoji: 'โ˜๏ธ' },
      filename: 'photo1.jpg',
      size: 2048576,
      mimeType: 'image/jpeg',
      checksum: 'abc123'
    },
    {
      source: { id: 'local-1', type: 'local', location: '/uploads', priority: 2, emoji: '๐Ÿ’ป' },
      filename: 'document.pdf',
      size: 1024000,
      mimeType: 'application/pdf',
      checksum: 'def456'
    },
    {
      source: { id: 'ftp-1', type: 'ftp', location: 'ftp.example.com', priority: 3, emoji: '๐Ÿ“ก' },
      filename: 'video.mp4',
      size: 10485760,
      mimeType: 'video/mp4',
      checksum: 'ghi789'
    },
    {
      source: { id: 'http-1', type: 'http', location: 'https://api.example.com', priority: 4, emoji: '๐ŸŒ' },
      filename: 'data.json',
      size: 512000,
      mimeType: 'application/json',
      checksum: 'jkl012'
    },
    {
      source: { id: 's3-2', type: 's3', location: 'bucket-2', priority: 1, emoji: 'โ˜๏ธ' },
      filename: 'image.png',
      size: 3072000,
      mimeType: 'image/png',
      checksum: 'mno345'
    }
  ];
  
  try {
    // ๐Ÿš€ Process the batch
    const report = await processor.processBatch(files);
    
    // ๐Ÿ“Š Display detailed results
    processor.displayDetailedReport(report);
    
  } catch (error) {
    console.error("๐Ÿ’ฅ File processing demo failed:", error);
  }
}

// ๐Ÿš€ Run the demo
demonstrateFileProcessing();

๐ŸŽ“ Key Takeaways

Youโ€™ve mastered Promise.allSettled! Hereโ€™s what you can now do:

  • โœ… Handle mixed success/failure scenarios gracefully without losing partial data ๐Ÿ’ช
  • โœ… Build resilient batch processing systems that work with partial results ๐Ÿ›ก๏ธ
  • โœ… Implement comprehensive error reporting while continuing operations ๐ŸŽฏ
  • โœ… Create fault-tolerant applications that donโ€™t fail at the first obstacle ๐Ÿš€
  • โœ… Design systems that embrace partial success rather than all-or-nothing approaches ๐Ÿ›

Remember: In the real world, some operations will always fail. Promise.allSettled helps you build systems that keep working despite these inevitable failures! ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered Promise.allSettled in TypeScript!

Hereโ€™s what to explore next:

  1. ๐Ÿ’ป Practice with the distributed file processing exercise above
  2. ๐Ÿ—๏ธ Build a comprehensive data validation system with partial success tracking
  3. ๐Ÿ“š Explore advanced Promise patterns like custom Promise schedulers
  4. ๐ŸŒŸ Learn about AsyncIterables and Observables for streaming data processing

Remember: Resilience isnโ€™t about never failing - itโ€™s about continuing to work when parts of your system do fail! ๐Ÿš€


Happy coding! ๐ŸŽ‰๐Ÿš€โœจ