Implementing Promise.race in JavaScript
Let's dive into implementing Promise.race
, another useful method in JavaScript's Promise API. This method takes an iterable of promises and returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.
What is Promise.race
?
Promise.race
is designed to return a promise that resolves or rejects as soon as one of the provided promises resolves or rejects. This is useful when you need to proceed with the first available result, whether it is a success or a failure.
Real Interview Insights
Interviewers might ask you to:
- Implement
Promise.race
from scratch. - Explain the behavior of
Promise.race
with various combinations of promises. - Discuss how
Promise.race
compares to other promise methods likePromise.all
andPromise.any
.
Implementing Promise.race
Here’s a basic implementation of Promise.race
:
function promiseRace(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
throw new TypeError('Input must be an array');
}
promises.forEach(promise => {
Promise.resolve(promise)
.then(resolve)
.catch(reject);
});
});
}
Explanation:
- Input Validation: Ensure the input is an array. If not, throw a
TypeError
. - Promise Processing: Use
Promise.resolve
to handle both promise and non-promise values. Resolve or reject the returned promise as soon as the first promise resolves or rejects.
Practical Example
Consider an example with multiple promises:
const promise1 = new Promise((resolve) => setTimeout(resolve, 500, 'First'));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'Second'));
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 200, 'Third'));
promiseRace([promise1, promise2, promise3])
.then(result => console.log(result)) // Output: 'Second'
.catch(error => console.error(error));
In this example:
- The
promiseRace
function returns the result ofpromise2
as it is the first promise to resolve, ignoring the results ofpromise1
andpromise3
.
Advanced Use Case: Handling Empty Arrays
To handle empty arrays gracefully, ensure your implementation checks for this case and resolves with an appropriate value:
function promiseRace(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
throw new TypeError('Input must be an array');
}
if (promises.length === 0) {
return resolve();
}
promises.forEach(promise => {
Promise.resolve(promise)
.then(resolve)
.catch(reject);
});
});
}
Performance Considerations
When implementing Promise.race
, consider:
- Early Resolution: The function resolves or rejects as soon as one of the promises settles, which is efficient.
- Error Handling: Be aware of how errors are propagated in the case of multiple rejections.
Coding Challenge: Enhancing Promise.race
with Timeout
Challenge: Enhance the promiseRace
function to include a timeout mechanism, allowing it to reject if no promises settle within a specified time limit.
function promiseRace(promises, timeout = 5000) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
throw new TypeError('Input must be an array');
}
if (promises.length === 0) {
return resolve();
}
let timeoutId = setTimeout(() => {
reject(new Error('Operation timed out'));
}, timeout);
promises.forEach(promise => {
Promise.resolve(promise)
.then(result => {
clearTimeout(timeoutId);
resolve(result);
})
.catch(error => {
clearTimeout(timeoutId);
reject(error);
});
});
});
}
// Example usage with timeout
const promise1 = new Promise((resolve) => setTimeout(resolve, 500, 'First'));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'Second'));
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 200, 'Third'));
promiseRace([promise1, promise2, promise3], 300)
.then(result => console.log(result)) // Output: 'Second'
.catch(error => console.error(error)); // If no promise resolves/rejects within 300ms
In this challenge:
- Add a timeout to reject the promise if no promises settle within the specified time.