Have you ever needed to handle multiple asynchronous operations but only care about the first one that completes? JavaScript’s Promise.race() is the perfect tool for this scenario, and today we’ll explore everything you need to know about this powerful feature.
Promise.race() takes an array of promises and returns a new promise that resolves or rejects as soon as the first promise in the array settles. It’s like organizing a race where only the first finisher matters – whether they succeed or fail.
Table of Contents
- What is Promise.race()?
- Basic Syntax
- Practical Examples
- Important Considerations
- Best Practices
- Common Use Cases
- Performance Considerations
- Conclusion
What is Promise.race()?
Promise.race() is a method that accepts an iterable of promises as input and returns a new promise. This new promise settles with the eventual state of the first promise that settles, whether it’s fulfilled or rejected.
Basic Syntax
Promise.race([promise1, promise2, promise3])
.then(result => {
console.log('First promise settled with:', result);
})
.catch(error => {
console.log('First promise failed with:', error);
});
Code language: JavaScript (javascript)
Practical Examples
Example 1: Implementing a Timeout
One of the most common use cases for Promise.race() is implementing timeouts for async operations:
const fetchWithTimeout = (url, timeout = 5000) => {
const fetchPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('Request timed out'));
}, timeout);
});
return Promise.race([fetchPromise, timeoutPromise]);
};
// Usage
fetchWithTimeout('https://api.example.com/data', 3000)
.then(response => response.json())
.then(data => console.log('Data:', data))
.catch(error => console.error('Error:', error));
Code language: JavaScript (javascript)
Example 2: Racing Multiple API Calls
Sometimes you might want to get data from the fastest responding server:
const servers = [
'https://api1.example.com/data',
'https://api2.example.com/data',
'https://api3.example.com/data'
];
const promises = servers.map(url =>
fetch(url).then(response => response.json())
);
Promise.race(promises)
.then(fastestData => {
console.log('First response:', fastestData);
})
.catch(error => {
console.error('Error:', error);
});
Code language: JavaScript (javascript)
Important Considerations
1. Empty Iterable
When called with an empty iterable, Promise.race() will forever remain in a pending state:
Promise.race([]).then(result => {
// This will never execute
console.log(result);
});
Code language: JavaScript (javascript)
2. Non-Promise Values
If you pass non-promise values in the iterable, they’ll be automatically wrapped in resolved promises:
Promise.race([1, Promise.resolve(2), Promise.resolve(3)])
.then(result => {
console.log(result); // Outputs: 1
});
Code language: JavaScript (javascript)
3. Error Handling
If the first promise to settle is rejected, the entire Promise.race() will reject:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => reject('Error!'), 100);
});
const promise2 = new Promise(resolve => {
setTimeout(() => resolve('Success!'), 200);
});
Promise.race([promise1, promise2])
.then(result => console.log(result))
.catch(error => console.error(error)); // Outputs: Error!
Code language: JavaScript (javascript)
Best Practices
1. Always Include Error Handling
const raceWithErrorHandling = promises => {
return Promise.race(promises)
.catch(error => {
console.error('Race failed:', error);
throw error; // Re-throw to maintain error chain
});
};
Code language: JavaScript (javascript)
2. Combine with Promise.all() When Needed
const getData = async () => {
const fastestResponse = await Promise.race(promises);
const allResponses = await Promise.all(promises);
return {
fastest: fastestResponse,
all: allResponses
};
};
Code language: JavaScript (javascript)
Common Use Cases
- Implementing request timeouts
- Racing multiple API endpoints
- User interaction timeouts
- Performance monitoring
- Resource loading with fallbacks
Performance Considerations
While Promise.race() is powerful, it’s important to note that all promises continue to execute even after one settles. For resource-intensive operations, you might want to implement cancellation:
const createCancellablePromise = promise => {
let cancel;
const wrappedPromise = new Promise((resolve, reject) => {
cancel = () => reject(new Error('Operation cancelled'));
promise.then(resolve, reject);
});
return {
promise: wrappedPromise,
cancel
};
};
Code language: JavaScript (javascript)
Conclusion
Promise.race() is a powerful tool in JavaScript’s asynchronous programming toolkit. When used properly, it can help you handle race conditions, implement timeouts, and create more responsive applications. Remember to always include proper error handling and consider the specific requirements of your use case when deciding between Promise.race() and other Promise methods.
While you’re exploring Promise methods, you might want to check out our article on JavaScript Promise.all() to learn about handling multiple promises that all need to complete successfully.
What’s your experience with Promise.race()? Have you found any interesting use cases in your projects? Share your thoughts and experiences in the comments below!