Implement Promise.race
This challenge asks you to recreate the functionality of JavaScript's built-in Promise.race() method. Promise.race() is crucial for handling scenarios where you need to know the result of the first promise that settles (either resolves or rejects) from a given collection of promises. This is useful for implementing timeouts, competing asynchronous operations, and more.
Problem Description
You need to implement a function named myPromiseRace that accepts an iterable (e.g., an array) of promises and returns a new promise. This new promise should settle (resolve or reject) as soon as the first promise in the iterable settles.
Key Requirements:
- The
myPromiseRacefunction should accept a single argument: an iterable (like an array) of promises. - It should return a new
Promise. - If any of the promises in the iterable resolve, the returned promise should resolve with the value of that first resolved promise.
- If any of the promises in the iterable reject, the returned promise should reject with the reason of that first rejected promise.
- The function should handle empty iterables correctly.
- The function should handle non-promise values within the iterable.
Expected Behavior:
- If the first promise to settle resolves with value
V,myPromiseRaceshould resolve withV. - If the first promise to settle rejects with reason
R,myPromiseRaceshould reject withR. - If the iterable is empty, the returned promise should remain pending forever (as per the standard
Promise.racebehavior).
Edge Cases:
- An iterable containing no promises.
- An iterable containing a mix of promises and non-promise values.
- An iterable where multiple promises settle almost simultaneously.
Examples
Example 1:
const p1 = new Promise((resolve) => setTimeout(() => resolve('one'), 200));
const p2 = new Promise((resolve, reject) => setTimeout(() => reject('two'), 100));
myPromiseRace([p1, p2])
.then(value => console.log(value))
.catch(reason => console.error(reason));
Output:
two
Explanation: p2 rejects with 'two' after 100ms, which is faster than p1 resolving with 'one' after 200ms. Therefore, myPromiseRace rejects with 'two'.
Example 2:
const p3 = new Promise((resolve) => setTimeout(() => resolve('three'), 150));
const p4 = new Promise((resolve) => setTimeout(() => resolve('four'), 250));
myPromiseRace([p3, p4])
.then(value => console.log(value))
.catch(reason => console.error(reason));
Output:
three
Explanation: p3 resolves with 'three' after 150ms, which is faster than p4 resolving with 'four' after 250ms. Therefore, myPromiseRace resolves with 'three'.
Example 3: (Handling non-promise values and immediate settlement)
const p5 = new Promise((resolve) => setTimeout(() => resolve('fast'), 50));
const nonPromiseValue = 'instant';
myPromiseRace([p5, nonPromiseValue])
.then(value => console.log(value))
.catch(reason => console.error(reason));
Output:
instant
Explanation: Non-promise values are treated as already resolved promises. Since 'instant' is encountered before p5 settles, myPromiseRace resolves immediately with 'instant'.
Example 4: (Empty iterable)
myPromiseRace([])
.then(value => console.log('Resolved:', value))
.catch(reason => console.error('Rejected:', reason));
// This promise will never settle.
Output: (No output will appear in the console, as the promise remains pending indefinitely.)
Explanation: When an empty iterable is provided, Promise.race conventionally returns a promise that never settles.
Constraints
- The input iterable can contain any number of promises or non-promise values, from zero to a very large number.
- The function must be implemented using only native JavaScript features and
Promiseconstructor. You cannot use the built-inPromise.race. - The implementation should be reasonably efficient and avoid unnecessary overhead.
Notes
- Remember that
Promise.race()settles as soon as any promise in the iterable settles, regardless of whether it resolves or rejects. - Consider how you will handle iterating through the input and attaching listeners to each promise.
- Think about how to ensure that once the
myPromiseRacepromise settles, no further actions from other promises affect its state. - Non-promise values in the iterable should be treated as promises that resolve immediately with that value.