Why and how to use Promise.allSettled

Why

There will be cases when you need to make multiple API calls and you want to respond after they have all completed and with the status of whether they were successfully fulfilled or not.

How Promise.allSettled works

  • It accepts an array of promises
  • It returns a promise that resolves to an array of objects
  • Each object represents the result of each promise, containing either a status of "fulfilled" and a value, or a status of "rejected" and a reason
  • Why use Promise.allSettled ?

    Promise.allSettled is particularly useful in scenarios where you need to wait for multiple asynchronous operations to complete, regardless of whether they succeed or fail. For example:

  • Aggregating results from multiple API calls
  • Performing parallel operations where individual failures should not stop the entire process
  • Collecting data from various sources where some might fail but the overall result is still useful
  • Examples

    Aggregating API calls

    type ApiResponse = { data: any };
    
    const fetchApi1 = (): Promise<ApiResponse> => {
      return new Promise((resolve, reject) => {
        // Simulate API call
        setTimeout(() => resolve({ data: 'API 1 response' }), 1000);
      });
    };
    
    const fetchApi2 = (): Promise<ApiResponse> => {
      return new Promise((resolve, reject) => {
        // Simulate API call
        setTimeout(() => reject(new Error('API 2 failed')), 1500);
      });
    };
    
    const fetchApi3 = (): Promise<ApiResponse> => {
      return new Promise((resolve, reject) => {
        // Simulate API call
        setTimeout(() => resolve({ data: 'API 3 response' }), 2000);
      });
    };
    
    const fetchAllApis = async () => {
      const results = await Promise.allSettled([fetchApi1(), fetchApi2(), fetchApi3()]);
    
      results.forEach((result, index) => {
        if (result.status === 'fulfilled') {
          console.log(`API ${index + 1} succeeded with response:`, result.value);
        } else {
          console.log(`API ${index + 1} failed with reason:`, result.reason);
        }
      });
    };
    
    fetchAllApis();

    Handling Multiple File Uploads

    type UploadResult = { filename: string; url: string };
    
    const uploadFile = (file: File): Promise<UploadResult> => {
      return new Promise((resolve, reject) => {
        // Simulate file upload
        const isSuccess = Math.random() > 0.5; // Random success or failure
        setTimeout(() => {
          if (isSuccess) {
            resolve({ filename: file.name, url: `https://example.com/${file.name}` });
          } else {
            reject(new Error(`Failed to upload ${file.name}`));
          }
        }, 1000);
      });
    };
    
    const uploadFiles = async (files: File[]) => {
      const results = await Promise.allSettled(files.map(uploadFile));
    
      results.forEach((result, index) => {
        if (result.status === 'fulfilled') {
          console.log(`File ${files[index].name} uploaded successfully:`, result.value);
        } else {
          console.error(`File ${files[index].name} failed to upload:`, result.reason);
        }
      });
    };
    
    // Example usage
    const files: File[] = [
      new File(['content'], 'file1.txt'),
      new File(['content'], 'file2.txt'),
      new File(['content'], 'file3.txt')
    ];
    
    uploadFiles(files);