What is Promise.any() for?

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Medium
#JavaScript #Asynchronicity #JS Basics

Brief Answer

Promise.any() is a static Promise method that takes an array of promises and returns a new promise that resolves with the value of the first successfully completed promise from the array. If all promises are rejected, it returns a rejected promise with AggregateError containing all errors. Useful for implementing fallback data sources, parallel requests, and performance improvements.

Main use cases:

  • Fallback sources — getting data from the first available source
  • Parallel requests — speeding up data retrieval through multiple sources
  • Handling successful results — ignoring errors when at least one successful result is present

Main Idea:

“First successful result!”

🎯 How It Works:

const promise1 = new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Error 1')), 500)
);
 
const promise2 = new Promise(resolve => 
    setTimeout(() => resolve('Success 2'), 200)
);
 
const promise3 = new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Error 3')), 300)
);
 
Promise.any([promise1, promise2, promise3])
    .then(result => console.log('Winner:', result))   // "Winner: Success 2"
    .catch(error => console.error('All failed:', error));

Full Answer

Promise.any() is a powerful tool in JavaScript asynchronous programming that allows executing multiple promises in parallel and working with the first successfully completed result. Unlike Promise.race(), which responds to the first completed promise (successful or not), Promise.any() waits for the first successful result.

How Promise.any() Works

Promise.any() takes an iterable object (usually an array) of promises and returns a new promise:

const promise1 = new Promise((_, reject) => setTimeout(() => reject(new Error('Error 1')), 1000));
const promise2 = new Promise((_, reject) => setTimeout(() => reject(new Error('Error 2')), 2000));
const promise3 = new Promise(resolve => setTimeout(() => resolve('Success 3'), 500));
 
Promise.any([promise1, promise2, promise3])
  .then(result => console.log(result)); // "Success 3" (first successful)

Main Use Cases

1. Fallback Data Sources

One of the most common uses of Promise.any() is getting data from the first available source:

function fetchFromMultipleSources(url) {
  const sources = [
    fetch(`https://fast-cdn.com${url}`),
    fetch(`https://backup-server.com${url}`),
    fetch(`https://mirror-site.com${url}`)
  ];
  
  return Promise.any(sources);
}
 
// Usage
fetchFromMultipleSources('/api/user-data')
  .then(response => response.json())
  .then(data => console.log('Data received from first available source:', data))
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('All sources unavailable:', error.errors);
    } else {
      console.error('Error:', error);
    }
  });

2. Parallel Requests for Speed

Useful when having multiple sources of the same data:

function fetchUserData(userId) {
  const requests = [
    fetch(`https://api1.example.com/users/${userId}`),
    fetch(`https://api2.example.com/users/${userId}`),
    fetch(`https://api3.example.com/users/${userId}`)
  ];
  
  return Promise.any(requests)
    .then(response => {
      if (!response.ok) throw new Error('Network error');
      return response.json();
    });
}
 
// Usage
fetchUserData(123)
  .then(userData => {
    console.log('User data from the fastest API:', userData);
  })
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('All APIs unavailable:', error.errors);
    }
  });

3. Service Availability Checking

Can be used to check availability of the first working service:

function checkServiceAvailability() {
  const services = [
    fetch('https://service1.example.com/health'),
    fetch('https://service2.example.com/health'),
    fetch('https://service3.example.com/health')
  ];
  
  return Promise.any(services)
    .then(response => {
      if (response.ok) {
        return 'Service available';
      } else {
        throw new Error('Service not responding');
      }
    });
}
 
// Usage
checkServiceAvailability()
  .then(status => console.log(status))
  .catch(error => {
    if (error instanceof AggregateError) {
      console.log('All services unavailable');
    }
  });

Practical Examples

Getting Data with Timeout

function fetchWithFallback(url, fallbackUrls, timeoutMs = 5000) {
  const mainRequest = fetch(url);
  const timeout = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Timeout')), timeoutMs);
  });
  
  const fallbackRequests = fallbackUrls.map(fbUrl => fetch(fbUrl));
  
  // First wait for main request or timeout
  return Promise.race([mainRequest, timeout])
    .catch(() => {
      // If main request failed, try fallbacks
      return Promise.any(fallbackRequests);
    });
}
 
// Usage
fetchWithFallback(
  '/api/data',
  ['/api/data-backup1', '/api/data-backup2']
)
  .then(response => response.json())
  .then(data => console.log('Data:', data))
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('All sources unavailable:', error.errors);
    } else {
      console.error('Error:', error.message);
    }
  });

Race Between Cache and Network

function fetchWithCachePreference(url) {
  const cacheRequest = getCachedData(url).then(data => {
    if (data) return data;
    throw new Error('No data in cache');
  });
  
  const networkRequest = fetch(url).then(response => response.json());
  
  return Promise.any([cacheRequest, networkRequest])
    .catch(error => {
      if (error instanceof AggregateError) {
        // If neither cache nor network work, try network again
        return networkRequest;
      }
      throw error;
    });
}
 
// Usage
fetchWithCachePreference('/api/user-profile')
  .then(data => {
    console.log('Data received (first available source):', data);
    return data;
  })
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('All sources unavailable:', error.errors);
    }
  });

Competition Between Multiple APIs

function fetchFromFastestWorkingAPI(endpoint) {
  const apis = [
    fetch(`https://api1.example.com${endpoint}`),
    fetch(`https://api2.example.com${endpoint}`),
    fetch(`https://api3.example.com${endpoint}`)
  ];
  
  return Promise.any(apis)
    .then(response => {
      if (!response.ok) throw new Error('API error');
      return response.json();
    });
}
 
// Usage
fetchFromFastestWorkingAPI('/users/123')
  .then(userData => {
    console.log('User data from first working API:', userData);
  })
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('All APIs unavailable:', error.errors);
    }
  });

Features and Behavior

1. Error Handling

If all promises are rejected, Promise.any() also rejects with AggregateError:

const errorPromise1 = new Promise((_, reject) => setTimeout(() => reject(new Error('Error 1')), 2000));
const errorPromise2 = new Promise((_, reject) => setTimeout(() => reject(new Error('Error 2')), 1000));
 
Promise.any([errorPromise1, errorPromise2])
  .then(result => console.log('Result:', result))
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('All promises failed:', error.errors);
    } else {
      console.error('Error:', error.message);
    }
  });

2. Empty Array

If passed an empty array, Promise.any() rejects with AggregateError:

// ❌ Will be rejected
Promise.any([])
  .then(result => console.log(result))
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('Empty array of promises');
    }
  });
 
// ✅ Check for empty array
function safeAny(promises) {
  if (promises.length === 0) {
    return Promise.reject(new Error('No promises to process'));
  }
  return Promise.any(promises);
}

3. Non-promise Values

Promise.any() converts non-promise values to resolved promises:

Promise.any([42, Promise.reject('Error'), new Promise(resolve => setTimeout(() => resolve(true), 1000))])
  .then(result => console.log(result)); // 42 (number instantly converted to resolved promise)

Common Mistakes and Solutions

1. Improper Handling of AggregateError

// ❌ May miss errors
Promise.any([fetch('/api/data1'), fetch('/api/data2')])
  .then(data => console.log(data));
 
// ✅ Proper handling
Promise.any([fetch('/api/data1'), fetch('/api/data2')])
  .then(response => {
    if (!response.ok) throw new Error('Network error');
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => {
    if (error instanceof AggregateError) {
      console.error('All requests failed:', error.errors);
    } else {
      console.error('Error:', error);
    }
  });

2. Ignoring Remaining Promises

// ❌ Remaining promises continue executing
const promises = [slowOperation(), fastOperation()];
Promise.any(promises)
  .then(result => {
    console.log('Fast result:', result);
    // slowOperation() still runs in background
  });
 
// ✅ Cancel remaining operations (using AbortController)
const controller = new AbortController();
const signal = controller.signal;
 
const promises = [
  fetch('/api/slow', { signal }),
  fetch('/api/fast', { signal })
];
 
Promise.any(promises)
  .then(response => {
    controller.abort(); // Cancel other requests
    return response.json();
  })
  .then(data => console.log(data));

Best Practices

  1. Always handle errors — use .catch() to handle AggregateError
  2. Use for fallback sources — effective way to get data from first available source
  3. Apply for parallel requests — speed up data retrieval through multiple sources
  4. Cancel unnecessary operations — save resources on first successful result
  5. Check empty arrays — avoid rejecting Promise.any()

Key Promise.any() Benefits

  1. Performance — instant reaction to first successful result
  2. Flexibility — supports various competition scenarios
  3. Ease of use — intuitive API
  4. Compatibility — works with all promise types
  5. Resource efficiency — ability to cancel remaining operations

Promise.any() is a powerful tool for optimizing asynchronous operations, allowing you to select the first available successful result from multiple parallel operations, which is especially useful for implementing fallback data sources and speeding up data retrieval through multiple sources.


Knowledge Check Task

Task

What will be output to the console and why?

const promise1 = new Promise((_, reject) => {
  setTimeout(() => reject(new Error('Error 1')), 100);
});
 
const promise2 = new Promise((_, reject) => {
  setTimeout(() => reject(new Error('Error 2')), 200);
});
 
const promise3 = new Promise(resolve => {
  setTimeout(() => resolve('Success 3'), 300);
});
 
Promise.any([promise1, promise2, promise3])
  .then(result => console.log('Result:', result))
  .catch(error => {
    if (error instanceof AggregateError) {
      console.log('All failed:', error.errors.length);
    } else {
      console.log('Other error:', error.message);
    }
  });
View answer

Answer: Result: Success 3

Explanation:

  1. All three promises start simultaneously
  2. promise1 rejects after 100ms with “Error 1”
  3. promise2 rejects after 200ms with “Error 2”
  4. promise3 resolves after 300ms with value “Success 3”
  5. Promise.any() returns the first successfully resolved promise, ignoring rejected ones
  6. Therefore, the result will be “Success 3”, and it will be output to the console

It’s important to understand that Promise.any() waits for the first successful result, not just the first completed one as in the case of Promise.race().


Want more articles to prepare for interviews? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪