+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 100 of 355

πŸ”— Promise Chaining: Sequential Async Operations Mastery

Master promise chaining in TypeScript for elegant sequential async operations, error handling, and complex data transformation workflows πŸš€

πŸš€Intermediate
22 min read

Prerequisites

  • Basic TypeScript syntax and types πŸ“
  • Understanding of Promises and async programming ⚑
  • Promise<T> types and error handling πŸ›‘οΈ

What you'll learn

  • Master promise chaining for sequential async operations πŸ”—
  • Handle complex data transformations with type safety ✨
  • Implement robust error handling in promise chains 🚨
  • Build elegant async workflows and data pipelines πŸ—οΈ

🎯 Introduction

Welcome to the art of promise chaining in TypeScript! πŸŽ‰ In this guide, we’ll explore how to create elegant sequences of asynchronous operations that flow like a well-orchestrated symphony.

You’ll discover how to chain promises together to create complex workflows that are both type-safe and maintainable. Whether you’re processing user data πŸ‘€, calling multiple APIs 🌐, or building data transformation pipelines 🏭, mastering promise chaining is essential for creating sophisticated async applications.

By the end of this tutorial, you’ll be crafting async sequences that are not just functional, but beautifully composed and impossible to break! πŸš€ Let’s dive in! πŸŠβ€β™‚οΈ

πŸ“š Understanding Promise Chaining

πŸ€” What is Promise Chaining?

Promise chaining is like creating a production line for async operations 🏭. Think of it as a series of conveyor belts where each step processes the output from the previous step, transforming data as it flows through your application.

In TypeScript terms, promise chaining provides:

  • ✨ Sequential execution - operations happen one after another
  • πŸš€ Data transformation - each step can modify the result
  • πŸ›‘οΈ Type safety - TypeScript tracks types through the chain
  • πŸ“¦ Clean syntax - readable, maintainable async code

πŸ’‘ Why Use Promise Chaining?

Here’s why promise chaining is powerful:

  1. Sequential Dependencies πŸ”—: When operation B needs result from A
  2. Data Transformation πŸ”„: Transform data through multiple steps
  3. Clean Error Handling 🚨: Single catch for the entire chain
  4. Readable Code πŸ“–: Linear flow that’s easy to follow
  5. Type Safety πŸ”’: Maintain types through complex transformations

Real-world example: When a user logs in πŸ”, you might: authenticate β†’ fetch profile β†’ load preferences β†’ redirect to dashboard. Each step depends on the previous one!

πŸ”§ Basic Promise Chaining Patterns

πŸ“ Simple Chaining with .then()

Let’s start with fundamental chaining patterns:

// 🎯 Basic promise chaining syntax
// Each .then() receives the result from the previous step

// πŸ“ Simple data transformation chain
const processUserInput = (input: string): Promise<string> => {
  return Promise.resolve(input)
    .then((data: string) => {
      // 🧹 Step 1: Clean the input
      console.log('🧹 Cleaning input:', data);
      return data.trim().toLowerCase();
    })
    .then((cleaned: string) => {
      // βœ… Step 2: Validate the input
      console.log('βœ… Validating input:', cleaned);
      if (cleaned.length < 3) {
        throw new Error('Input too short! πŸ“');
      }
      return cleaned;
    })
    .then((validated: string) => {
      // 🎨 Step 3: Format the output
      console.log('🎨 Formatting output:', validated);
      return `processed_${validated}`;
    });
};

// πŸš€ Usage
processUserInput('  Hello World  ')
  .then(result => {
    console.log('✨ Final result:', result); // processed_hello world
  })
  .catch(error => {
    console.error('πŸ’₯ Error:', error.message);
  });

πŸ”„ Chaining with Type Transformations

// 🎯 Complex type transformations through chaining
interface RawUser {
  id: number;
  name: string;
  email: string;
  created_at: string;
}

interface ProcessedUser {
  id: string;
  displayName: string;
  email: string;
  memberSince: Date;
  isActive: boolean;
}

interface UserProfile {
  user: ProcessedUser;
  permissions: string[];
  preferences: Record<string, any>;
}

// 🏭 Multi-step user processing pipeline
const processUserData = (rawUser: RawUser): Promise<UserProfile> => {
  return Promise.resolve(rawUser)
    .then((raw: RawUser): ProcessedUser => {
      // πŸ”„ Step 1: Transform user data
      console.log('πŸ”„ Transforming user data for:', raw.name);
      
      return {
        id: `user_${raw.id}`,
        displayName: raw.name.split(' ')
          .map(part => part.charAt(0).toUpperCase() + part.slice(1))
          .join(' '),
        email: raw.email.toLowerCase(),
        memberSince: new Date(raw.created_at),
        isActive: true
      };
    })
    .then((user: ProcessedUser): Promise<UserProfile> => {
      // πŸ” Step 2: Load user permissions (async operation)
      console.log('πŸ” Loading permissions for:', user.displayName);
      
      return loadUserPermissions(user.id).then(permissions => ({
        user,
        permissions,
        preferences: {} // Initialize empty preferences
      }));
    })
    .then((profile: UserProfile): Promise<UserProfile> => {
      // βš™οΈ Step 3: Load user preferences (another async operation)
      console.log('βš™οΈ Loading preferences for:', profile.user.displayName);
      
      return loadUserPreferences(profile.user.id).then(preferences => ({
        ...profile,
        preferences
      }));
    });
};

// πŸ› οΈ Helper functions for async operations
const loadUserPermissions = async (userId: string): Promise<string[]> => {
  // πŸ” Simulate API call
  await new Promise(resolve => setTimeout(resolve, 100));
  
  return ['read', 'write', 'delete'];
};

const loadUserPreferences = async (userId: string): Promise<Record<string, any>> => {
  // πŸ” Simulate API call  
  await new Promise(resolve => setTimeout(resolve, 150));
  
  return {
    theme: 'dark',
    language: 'en',
    notifications: true
  };
};

🌐 Real-World API Chaining Examples

πŸ“‘ Multi-API Data Aggregation

// 🎯 Real-world example: E-commerce order processing
interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
}

interface User {
  id: string;
  name: string;
  email: string;
  shippingAddress: Address;
}

interface Address {
  street: string;
  city: string;
  country: string;
  zipCode: string;
}

interface Order {
  id: string;
  user: User;
  products: Product[];
  total: number;
  shippingCost: number;
  tax: number;
  finalTotal: number;
}

interface ProcessedOrder {
  order: Order;
  paymentIntentId: string;
  trackingNumber: string;
  estimatedDelivery: Date;
}

// πŸ›’ Complex order processing pipeline
const processOrder = (userId: string, productIds: string[]): Promise<ProcessedOrder> => {
  return fetchUser(userId)
    .then((user: User) => {
      // πŸ‘€ Step 1: Get user data
      console.log('πŸ‘€ User loaded:', user.name);
      
      // πŸ“¦ Step 2: Fetch all products in parallel, then continue chain
      return Promise.all(productIds.map(id => fetchProduct(id)))
        .then((products: Product[]) => ({ user, products }));
    })
    .then(({ user, products }) => {
      // πŸ’° Step 3: Calculate order totals
      console.log('πŸ’° Calculating totals for', products.length, 'products');
      
      const subtotal = products.reduce((sum, product) => sum + product.price, 0);
      const tax = subtotal * 0.08; // 8% tax
      const shippingCost = calculateShipping(user.shippingAddress, products);
      const finalTotal = subtotal + tax + shippingCost;
      
      const order: Order = {
        id: `order_${Date.now()}`,
        user,
        products,
        total: subtotal,
        shippingCost,
        tax,
        finalTotal
      };
      
      return order;
    })
    .then((order: Order) => {
      // πŸ’³ Step 4: Process payment
      console.log('πŸ’³ Processing payment for $', order.finalTotal);
      
      return processPayment(order).then(paymentIntentId => ({
        order,
        paymentIntentId
      }));
    })
    .then(({ order, paymentIntentId }) => {
      // 🚚 Step 5: Create shipping label
      console.log('🚚 Creating shipping label');
      
      return createShippingLabel(order).then(trackingNumber => ({
        order,
        paymentIntentId,
        trackingNumber
      }));
    })
    .then(({ order, paymentIntentId, trackingNumber }) => {
      // πŸ“… Step 6: Calculate delivery estimate
      console.log('πŸ“… Calculating delivery estimate');
      
      const estimatedDelivery = calculateDeliveryDate(
        order.user.shippingAddress,
        order.products
      );
      
      const processedOrder: ProcessedOrder = {
        order,
        paymentIntentId,
        trackingNumber,
        estimatedDelivery
      };
      
      return processedOrder;
    });
};

// πŸ› οΈ Mock API functions
const fetchUser = async (userId: string): Promise<User> => {
  await new Promise(resolve => setTimeout(resolve, 200));
  return {
    id: userId,
    name: 'John Doe',
    email: '[email protected]',
    shippingAddress: {
      street: '123 Main St',
      city: 'New York',
      country: 'USA',
      zipCode: '10001'
    }
  };
};

const fetchProduct = async (productId: string): Promise<Product> => {
  await new Promise(resolve => setTimeout(resolve, 100));
  return {
    id: productId,
    name: `Product ${productId}`,
    price: Math.floor(Math.random() * 100) + 10,
    category: 'Electronics'
  };
};

const processPayment = async (order: Order): Promise<string> => {
  await new Promise(resolve => setTimeout(resolve, 300));
  return `pi_${Math.random().toString(36).substr(2, 9)}`;
};

const createShippingLabel = async (order: Order): Promise<string> => {
  await new Promise(resolve => setTimeout(resolve, 200));
  return `TRK${Math.random().toString(36).substr(2, 8).toUpperCase()}`;
};

const calculateShipping = (address: Address, products: Product[]): number => {
  const baseRate = 5.99;
  const weightRate = products.length * 1.50;
  return Math.round((baseRate + weightRate) * 100) / 100;
};

const calculateDeliveryDate = (address: Address, products: Product[]): Date => {
  const daysToAdd = address.country === 'USA' ? 3 : 7;
  const deliveryDate = new Date();
  deliveryDate.setDate(deliveryDate.getDate() + daysToAdd);
  return deliveryDate;
};

🚨 Error Handling in Promise Chains

πŸ›‘οΈ Comprehensive Error Management

// 🎯 Robust error handling strategies
interface ApiError {
  code: string;
  message: string;
  statusCode: number;
  timestamp: Date;
}

interface RetryOptions {
  maxRetries: number;
  delayMs: number;
  backoffMultiplier: number;
}

// πŸ”„ Promise chain with retry logic and error recovery
const robustApiCall = <T>(
  operation: () => Promise<T>,
  retryOptions: RetryOptions = {
    maxRetries: 3,
    delayMs: 1000,
    backoffMultiplier: 2
  }
): Promise<T> => {
  let attempt = 0;
  
  const executeWithRetry = (): Promise<T> => {
    return operation()
      .catch((error: Error) => {
        attempt++;
        
        // 🚨 Log the error attempt
        console.error(`❌ Attempt ${attempt} failed:`, error.message);
        
        if (attempt >= retryOptions.maxRetries) {
          // πŸ›‘ Max retries reached, give up
          throw new Error(`Operation failed after ${retryOptions.maxRetries} attempts: ${error.message}`);
        }
        
        // ⏱️ Calculate delay with exponential backoff
        const delay = retryOptions.delayMs * Math.pow(retryOptions.backoffMultiplier, attempt - 1);
        
        console.log(`⏳ Retrying in ${delay}ms... (attempt ${attempt + 1}/${retryOptions.maxRetries})`);
        
        // πŸ• Wait and retry
        return new Promise<T>((resolve) => {
          setTimeout(() => {
            resolve(executeWithRetry());
          }, delay);
        });
      });
  };
  
  return executeWithRetry();
};

// πŸ₯ Error recovery and fallback strategies
const resilientDataPipeline = (userId: string): Promise<UserProfile> => {
  return Promise.resolve(userId)
    .then((id: string) => {
      // 🎯 Step 1: Fetch user with retry logic
      console.log('πŸ‘€ Fetching user data...');
      
      return robustApiCall(() => fetchUserWithError(id));
    })
    .then((user: ProcessedUser) => {
      // πŸ” Step 2: Try to load permissions, with fallback
      console.log('πŸ” Loading user permissions...');
      
      return loadUserPermissions(user.id)
        .catch((error: Error) => {
          // 🚨 If permissions fail, provide default permissions
          console.warn('⚠️ Failed to load permissions, using defaults:', error.message);
          return ['read']; // Default fallback permissions
        })
        .then((permissions: string[]) => ({ user, permissions }));
    })
    .then(({ user, permissions }) => {
      // βš™οΈ Step 3: Try to load preferences, with fallback
      console.log('βš™οΈ Loading user preferences...');
      
      return loadUserPreferences(user.id)
        .catch((error: Error) => {
          // 🚨 If preferences fail, provide defaults
          console.warn('⚠️ Failed to load preferences, using defaults:', error.message);
          return { theme: 'light', language: 'en' }; // Default preferences
        })
        .then((preferences: Record<string, any>) => ({
          user,
          permissions,
          preferences
        }));
    })
    .catch((error: Error) => {
      // πŸ’₯ Final error handler for the entire chain
      console.error('πŸ’₯ Pipeline failed completely:', error.message);
      
      // πŸ”„ Create minimal profile for graceful degradation
      const fallbackProfile: UserProfile = {
        user: {
          id: userId,
          displayName: 'Unknown User',
          email: '[email protected]',
          memberSince: new Date(),
          isActive: false
        },
        permissions: ['read'],
        preferences: { theme: 'light' }
      };
      
      return fallbackProfile;
    });
};

// 🎭 Mock function that randomly fails for testing
const fetchUserWithError = async (userId: string): Promise<ProcessedUser> => {
  await new Promise(resolve => setTimeout(resolve, 200));
  
  // 🎲 30% chance of failure for testing
  if (Math.random() < 0.3) {
    throw new Error(`User service temporarily unavailable 🚫`);
  }
  
  return {
    id: userId,
    displayName: 'John Doe',
    email: '[email protected]',
    memberSince: new Date(),
    isActive: true
  };
};

πŸ”„ Advanced Chaining Patterns

🎨 Conditional Chaining and Branching

// 🎯 Dynamic promise chains based on conditions
interface TaskResult {
  success: boolean;
  data?: any;
  error?: string;
  timestamp: Date;
}

interface ProcessingContext {
  userId: string;
  taskType: 'premium' | 'standard' | 'basic';
  priority: number;
  requiresApproval: boolean;
}

// 🌿 Conditional processing pipeline
const conditionalProcessing = (context: ProcessingContext): Promise<TaskResult> => {
  return Promise.resolve(context)
    .then((ctx: ProcessingContext) => {
      // 🎯 Step 1: Basic validation
      console.log('πŸ” Validating context for user:', ctx.userId);
      
      if (!ctx.userId) {
        throw new Error('User ID is required πŸ“');
      }
      
      return ctx;
    })
    .then((ctx: ProcessingContext) => {
      // 🏷️ Step 2: Conditional processing based on task type
      console.log('🏷️ Processing', ctx.taskType, 'task');
      
      switch (ctx.taskType) {
        case 'premium':
          return processPremiumTask(ctx);
        case 'standard':
          return processStandardTask(ctx);
        case 'basic':
          return processBasicTask(ctx);
        default:
          throw new Error(`Unknown task type: ${ctx.taskType} πŸ€”`);
      }
    })
    .then((result: any) => ({
      success: true,
      data: result,
      timestamp: new Date()
    }))
    .catch((error: Error) => ({
      success: false,
      error: error.message,
      timestamp: new Date()
    }));
};

// πŸ’Ž Premium task processing with extra steps
const processPremiumTask = (context: ProcessingContext): Promise<any> => {
  return Promise.resolve(context)
    .then((ctx) => {
      // πŸš€ Premium feature: Priority processing
      console.log('πŸš€ Applying premium priority processing');
      return { ...ctx, priorityApplied: true };
    })
    .then((ctx) => {
      // 🎨 Premium feature: Advanced analytics
      console.log('πŸ“Š Running premium analytics');
      return runAdvancedAnalytics(ctx);
    })
    .then((analyticsResult) => {
      // ✨ Premium feature: Custom notifications
      console.log('πŸ“± Sending premium notifications');
      return sendPremiumNotification(analyticsResult);
    });
};

// πŸ“Š Standard task processing
const processStandardTask = (context: ProcessingContext): Promise<any> => {
  return Promise.resolve(context)
    .then((ctx) => {
      console.log('πŸ“Š Running standard processing');
      return { processedAt: new Date(), type: 'standard' };
    })
    .then((result) => {
      // πŸ”” Standard notifications
      console.log('πŸ”” Sending standard notification');
      return { ...result, notificationSent: true };
    });
};

// πŸ“ Basic task processing
const processBasicTask = (context: ProcessingContext): Promise<any> => {
  return Promise.resolve({
    processedAt: new Date(),
    type: 'basic',
    message: 'Basic task completed βœ…'
  });
};

// πŸ› οΈ Helper functions
const runAdvancedAnalytics = async (context: any): Promise<any> => {
  await new Promise(resolve => setTimeout(resolve, 500));
  return { ...context, analyticsScore: Math.random() * 100 };
};

const sendPremiumNotification = async (data: any): Promise<any> => {
  await new Promise(resolve => setTimeout(resolve, 200));
  return { ...data, premiumNotificationSent: true };
};

πŸ”„ Parallel Processing within Chains

// 🎯 Combining parallel and sequential operations
interface DataSource {
  id: string;
  type: 'api' | 'database' | 'cache';
  priority: number;
}

interface AggregatedData {
  userId: string;
  profileData: any;
  activityData: any;
  settingsData: any;
  combinedAt: Date;
}

// 🏭 Mixed parallel/sequential data aggregation
const aggregateUserData = (userId: string): Promise<AggregatedData> => {
  return Promise.resolve(userId)
    .then((id: string) => {
      // 🎯 Step 1: Sequential - validate user exists
      console.log('βœ… Validating user exists:', id);
      return validateUserExists(id);
    })
    .then((validUserId: string) => {
      // πŸš€ Step 2: Parallel - fetch multiple data sources simultaneously
      console.log('πŸš€ Fetching data from multiple sources...');
      
      const dataPromises = [
        fetchUserProfile(validUserId),
        fetchUserActivity(validUserId),
        fetchUserSettings(validUserId)
      ];
      
      return Promise.all(dataPromises).then(([profileData, activityData, settingsData]) => ({
        userId: validUserId,
        profileData,
        activityData,
        settingsData
      }));
    })
    .then(({ userId, profileData, activityData, settingsData }) => {
      // πŸ”„ Step 3: Sequential - process and combine data
      console.log('πŸ”„ Processing and combining data...');
      
      return processAndCombineData(profileData, activityData, settingsData)
        .then((combinedData) => ({
          userId,
          ...combinedData,
          combinedAt: new Date()
        }));
    })
    .then((aggregatedData: AggregatedData) => {
      // πŸ’Ύ Step 4: Sequential - cache the result
      console.log('πŸ’Ύ Caching aggregated data...');
      
      return cacheAggregatedData(aggregatedData).then(() => aggregatedData);
    });
};

// πŸ› οΈ Helper functions for the pipeline
const validateUserExists = async (userId: string): Promise<string> => {
  await new Promise(resolve => setTimeout(resolve, 100));
  if (!userId || userId.length < 3) {
    throw new Error('Invalid user ID 🚫');
  }
  return userId;
};

const fetchUserProfile = async (userId: string): Promise<any> => {
  console.log('πŸ‘€ Fetching profile...');
  await new Promise(resolve => setTimeout(resolve, 200));
  return { name: 'John Doe', email: '[email protected]' };
};

const fetchUserActivity = async (userId: string): Promise<any> => {
  console.log('πŸ“Š Fetching activity...');
  await new Promise(resolve => setTimeout(resolve, 300));
  return { lastLogin: new Date(), sessionsCount: 42 };
};

const fetchUserSettings = async (userId: string): Promise<any> => {
  console.log('βš™οΈ Fetching settings...');
  await new Promise(resolve => setTimeout(resolve, 150));
  return { theme: 'dark', language: 'en' };
};

const processAndCombineData = async (profile: any, activity: any, settings: any): Promise<any> => {
  await new Promise(resolve => setTimeout(resolve, 100));
  return {
    profileData: profile,
    activityData: activity,
    settingsData: settings,
    processed: true
  };
};

const cacheAggregatedData = async (data: AggregatedData): Promise<void> => {
  await new Promise(resolve => setTimeout(resolve, 50));
  console.log('βœ… Data cached successfully');
};

🎯 Practical Exercise: Blog Post Publishing Pipeline

// 🎯 Complete blog publishing workflow
interface BlogPost {
  id: string;
  title: string;
  content: string;
  authorId: string;
  tags: string[];
  status: 'draft' | 'review' | 'published';
  createdAt: Date;
  publishedAt?: Date;
}

interface Author {
  id: string;
  name: string;
  email: string;
  isVerified: boolean;
  publishingRights: boolean;
}

interface PublishResult {
  post: BlogPost;
  seoScore: number;
  socialMediaPosted: boolean;
  notificationsSent: string[];
  publicUrl: string;
}

// πŸ“ Complete blog publishing pipeline
const publishBlogPost = (postId: string): Promise<PublishResult> => {
  return Promise.resolve(postId)
    .then((id: string) => {
      // πŸ“„ Step 1: Fetch the blog post
      console.log('πŸ“„ Fetching blog post:', id);
      return fetchBlogPost(id);
    })
    .then((post: BlogPost) => {
      // πŸ‘€ Step 2: Verify author permissions
      console.log('πŸ‘€ Verifying author permissions for:', post.authorId);
      
      return fetchAuthor(post.authorId).then((author: Author) => {
        if (!author.isVerified || !author.publishingRights) {
          throw new Error(`Author ${author.name} lacks publishing permissions 🚫`);
        }
        return { post, author };
      });
    })
    .then(({ post, author }) => {
      // βœ… Step 3: Content validation and SEO analysis
      console.log('βœ… Validating content and analyzing SEO...');
      
      return Promise.all([
        validateContent(post),
        analyzeSEO(post)
      ]).then(([isValid, seoScore]) => {
        if (!isValid) {
          throw new Error('Content validation failed πŸ“');
        }
        return { post, author, seoScore };
      });
    })
    .then(({ post, author, seoScore }) => {
      // 🌐 Step 4: Publish the post
      console.log('🌐 Publishing blog post...');
      
      const publishedPost: BlogPost = {
        ...post,
        status: 'published',
        publishedAt: new Date()
      };
      
      return savePublishedPost(publishedPost).then(() => ({
        post: publishedPost,
        author,
        seoScore
      }));
    })
    .then(({ post, author, seoScore }) => {
      // πŸ“± Step 5: Social media and notifications (parallel)
      console.log('πŸ“± Posting to social media and sending notifications...');
      
      return Promise.all([
        postToSocialMedia(post),
        sendNotifications(post, author)
      ]).then(([socialMediaPosted, notificationsSent]) => ({
        post,
        seoScore,
        socialMediaPosted,
        notificationsSent
      }));
    })
    .then(({ post, seoScore, socialMediaPosted, notificationsSent }) => {
      // πŸ”— Step 6: Generate public URL
      console.log('πŸ”— Generating public URL...');
      
      const publicUrl = generatePublicUrl(post);
      
      const result: PublishResult = {
        post,
        seoScore,
        socialMediaPosted,
        notificationsSent,
        publicUrl
      };
      
      return result;
    });
};

// πŸ› οΈ Mock implementation functions
const fetchBlogPost = async (postId: string): Promise<BlogPost> => {
  await new Promise(resolve => setTimeout(resolve, 100));
  return {
    id: postId,
    title: 'How to Master Promise Chaining',
    content: 'Promise chaining is a powerful pattern...',
    authorId: 'author_123',
    tags: ['typescript', 'async', 'promises'],
    status: 'review',
    createdAt: new Date(Date.now() - 86400000) // 1 day ago
  };
};

const fetchAuthor = async (authorId: string): Promise<Author> => {
  await new Promise(resolve => setTimeout(resolve, 50));
  return {
    id: authorId,
    name: 'Jane Developer',
    email: '[email protected]',
    isVerified: true,
    publishingRights: true
  };
};

const validateContent = async (post: BlogPost): Promise<boolean> => {
  await new Promise(resolve => setTimeout(resolve, 200));
  return post.content.length > 100 && post.title.length > 5;
};

const analyzeSEO = async (post: BlogPost): Promise<number> => {
  await new Promise(resolve => setTimeout(resolve, 300));
  // Mock SEO score calculation
  let score = 50;
  if (post.tags.length > 0) score += 20;
  if (post.title.length > 20) score += 15;
  if (post.content.length > 1000) score += 15;
  
  return Math.min(score, 100);
};

const savePublishedPost = async (post: BlogPost): Promise<void> => {
  await new Promise(resolve => setTimeout(resolve, 100));
  console.log('πŸ’Ύ Post saved to database');
};

const postToSocialMedia = async (post: BlogPost): Promise<boolean> => {
  await new Promise(resolve => setTimeout(resolve, 400));
  console.log('πŸ“± Posted to Twitter and LinkedIn');
  return true;
};

const sendNotifications = async (post: BlogPost, author: Author): Promise<string[]> => {
  await new Promise(resolve => setTimeout(resolve, 200));
  console.log('πŸ“§ Notifications sent');
  return ['email_subscribers', 'push_notifications', 'slack_team'];
};

const generatePublicUrl = (post: BlogPost): string => {
  const slug = post.title.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
  return `https://blog.example.com/posts/${slug}`;
};

// πŸš€ Usage example
publishBlogPost('post_123')
  .then((result: PublishResult) => {
    console.log('πŸŽ‰ Blog post published successfully!');
    console.log('πŸ“Š SEO Score:', result.seoScore);
    console.log('πŸ”— Public URL:', result.publicUrl);
    console.log('πŸ“’ Notifications:', result.notificationsSent.join(', '));
  })
  .catch((error: Error) => {
    console.error('πŸ’₯ Publishing failed:', error.message);
  });

🏁 Conclusion

Congratulations! πŸŽ‰ You’ve mastered the art of promise chaining in TypeScript. You now have the skills to:

  • βœ… Chain promises for elegant sequential async operations
  • πŸ”„ Transform data through complex multi-step pipelines
  • 🚨 Handle errors robustly with fallbacks and retry logic
  • πŸš€ Combine patterns mixing parallel and sequential operations
  • πŸ—οΈ Build workflows that are maintainable and type-safe

You’ve learned to create sophisticated async sequences that handle real-world complexity while maintaining clean, readable code. Keep practicing these patterns, and you’ll be the async flow master your team needs! πŸ†

πŸ”— Next Steps

Ready to level up further? Check out these advanced topics:

  • πŸš€ Promise.all() for parallel operations and performance
  • ⚑ Async/Await syntax for even cleaner async code
  • πŸ”„ RxJS Observables for reactive programming patterns
  • 🏭 Stream Processing for handling large data flows
  • 🎯 Error Boundaries for bulletproof error handling