Implement Promise.any in JavaScript
The Promise.any() static method takes an iterable of Promises as input and returns a single Promise that resolves as soon as any of the input Promises resolve. If all input Promises reject, the returned Promise rejects with an AggregateError. This functionality is crucial for scenarios where you need to get the result from the fastest-resolving asynchronous operation.
Problem Description
Your task is to implement a custom Promise.any() function in JavaScript that mimics the behavior of the native Promise.any().
What needs to be achieved:
Create a function, let's call it myPromiseAny, that accepts an iterable (like an array) of Promises. This function should return a new Promise.
Key requirements:
- First to Resolve Wins: If any of the Promises in the input iterable resolve,
myPromiseAnyshould immediately resolve with the value of the first Promise that resolves. - Aggregate Rejection: If all Promises in the input iterable reject,
myPromiseAnyshould reject with anAggregateError. This error object should contain anerrorsproperty, which is an array of all the rejection reasons from the input Promises. - Empty Iterable: If the input iterable is empty,
myPromiseAnyshould reject immediately with anAggregateErrorcontaining an emptyerrorsarray. - Non-Promise Iterables: The function should handle cases where the iterable contains non-Promise values. These values should be treated as already-resolved Promises.
Expected behavior:
myPromiseAny([p1, p2, p3])should resolve with the value of the firstpthat resolves.myPromiseAny([p1, p2, p3])should reject with anAggregateErrorif allpreject.myPromiseAny([])should reject with anAggregateErrorwith an emptyerrorsarray.
Important edge cases to consider:
- Handling an empty input array.
- The order of resolution and rejection.
- Iterables that contain a mix of Promises and non-Promise values.
- The structure of the
AggregateErrorobject.
Examples
Example 1:
Input: [Promise.resolve(3), new Promise((resolve, reject) => setTimeout(reject, 100, 'foo')), new Promise((resolve, reject) => setTimeout(resolve, 200, 'bar'))]
Output: Promise resolves with the value 3
Explanation: The first Promise in the array resolves immediately with the value 3. Therefore, myPromiseAny resolves with 3 without waiting for the other Promises.
Example 2:
Input: [Promise.reject('error1'), Promise.reject('error2'), Promise.reject('error3')]
Output: Promise rejects with an AggregateError
Explanation: All Promises in the input array reject. myPromiseAny collects all the rejection reasons ('error1', 'error2', 'error3') into an AggregateError and rejects with it. The AggregateError will have an `errors` property containing ['error1', 'error2', 'error3'].
Example 3:
Input: [1, Promise.reject('error1'), Promise.resolve(2)]
Output: Promise resolves with the value 1
Explanation: The first element is not a Promise, so it's treated as an already resolved Promise with value 1. myPromiseAny resolves immediately with 1.
Example 4:
Input: []
Output: Promise rejects with an AggregateError
Explanation: An empty iterable is provided. myPromiseAny rejects immediately with an AggregateError whose `errors` property is an empty array.
Constraints
- The input will be an iterable (e.g., an array) of Promises or values.
- The implementation should be in plain JavaScript without using the native
Promise.any()method. - You may use
Promise.resolve(),Promise.reject(), and thePromiseconstructor. - The
AggregateErrorobject should be correctly instantiated and populated. IfAggregateErroris not natively available in the execution environment, you can create a custom error class that mimics its structure (e.g., a plain Error object with anerrorsproperty).
Notes
Consider using Promise.resolve() to wrap any non-Promise values in the input iterable to unify your handling logic.
Keep track of how many Promises have rejected. You'll need this count to determine when all Promises have rejected.
Think about how to manage the asynchronous nature of Promises and ensure that myPromiseAny resolves or rejects only once.