阿里云主机折上折
  • 微信号
Current Site:Index > The Promise.race() method

The Promise.race() method

Author:Chuan Chen 阅读数:40758人阅读 分类: JavaScript

Basic Concept of Promise.race()

Promise.race() is a static method of the Promise object in ECMAScript 6. It takes an iterable collection of Promise objects as an argument and returns a new Promise instance. The state and value of this new Promise will be determined by the first Promise that changes its state, whether it is fulfilled or rejected.

const promise1 = new Promise((resolve) => setTimeout(resolve, 500, 'one'));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'two'));

Promise.race([promise1, promise2]).then((value) => {
  console.log(value); // Output: 'two'
});

Method Parameters and Return Value

The Promise.race() method accepts an iterable object as a parameter, typically an array containing multiple Promises. If the elements in this iterable object are not Promise instances, they will be automatically converted to Promises via Promise.resolve().

// Non-Promise values are automatically converted
Promise.race([1, 2, 3]).then((value) => {
  console.log(value); // Output: 1
});

// Mixed Promises and non-Promise values
const promise3 = new Promise((resolve) => setTimeout(resolve, 1000, 'slow'));
Promise.race([promise3, 'fast']).then((value) => {
  console.log(value); // Output: 'fast'
});

Practical Use Cases

Request Timeout Handling

Promise.race() is commonly used to implement request timeout mechanisms by racing an asynchronous request against a timer Promise.

function fetchWithTimeout(url, timeout = 5000) {
  const fetchPromise = fetch(url);
  const timeoutPromise = new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Request timeout')), timeout)
  );
  
  return Promise.race([fetchPromise, timeoutPromise]);
}

fetchWithTimeout('https://api.example.com/data')
  .then(response => response.json())
  .catch(error => console.error(error)); // Throws an error if no response within 5 seconds

Competing Data Sources

When multiple data sources can provide the same data, Promise.race() can be used to retrieve the fastest responding source.

const source1 = fetch('https://source1.example.com/data');
const source2 = fetch('https://source2.example.com/data');
const source3 = fetch('https://source3.example.com/data');

Promise.race([source1, source2, source3])
  .then(response => response.json())
  .then(data => console.log('Fastest source data:', data));

Error Handling Mechanism

Promise.race() immediately reflects the state of the first settled Promise, including rejected states. This means if the first completed Promise is rejected, the entire race result will be rejected.

const successPromise = new Promise((resolve) => setTimeout(resolve, 200, 'Success'));
const errorPromise = new Promise((_, reject) => setTimeout(reject, 100, new Error('Failed')));

Promise.race([successPromise, errorPromise])
  .then(value => console.log(value))
  .catch(error => console.error(error.message)); // Output: 'Failed'

Differences from Promise.all()

Promise.race() and Promise.all() are both methods for handling multiple Promises, but their behaviors are entirely different:

  • Promise.race(): Returns the result of the first settled Promise.
  • Promise.all(): Waits for all Promises to complete and returns an array of all results; if any Promise is rejected, it immediately rejects.
// Promise.race() example
Promise.race([
  Promise.resolve(1),
  Promise.reject(new Error('2')),
  Promise.resolve(3)
])
.then(console.log) // Output: 1
.catch(console.error);

// Promise.all() example
Promise.all([
  Promise.resolve(1),
  Promise.reject(new Error('2')),
  Promise.resolve(3)
])
.then(console.log)
.catch(console.error); // Output: Error: 2

Advanced Usage Examples

Canceling Long-Running Operations

Combined with AbortController, it can cancel long-running operations.

function cancellableRace(promise, timeout) {
  const controller = new AbortController();
  const timeoutPromise = new Promise((_, reject) => 
    setTimeout(() => {
      controller.abort();
      reject(new Error('Operation cancelled'));
    }, timeout)
  );
  
  promise.signal = controller.signal;
  
  return Promise.race([promise, timeoutPromise]);
}

const longRunningTask = new Promise((resolve) => {
  // Simulate a long-running task
  setTimeout(resolve, 10000, 'Task completed');
});

cancellableRace(longRunningTask, 2000)
  .then(console.log)
  .catch(console.error); // Output after 2 seconds: Error: Operation cancelled

Parallel Execution of Multiple Strategies

When multiple strategies can accomplish the same task, use Promise.race() to select the fastest successful strategy.

function getData() {
  const strategy1 = new Promise((resolve) => 
    setTimeout(resolve, 300, {source: 'Cache', data: 'cached data'})
  );
  
  const strategy2 = new Promise((resolve) => 
    setTimeout(resolve, 200, {source: 'API', data: 'api data'})
  );
  
  const strategy3 = new Promise((resolve) => 
    setTimeout(resolve, 400, {source: 'LocalStorage', data: 'local data'})
  );
  
  return Promise.race([strategy1, strategy2, strategy3]);
}

getData().then(result => {
  console.log(`Data from ${result.source}: ${result.data}`);
  // Output: Data from API: api data
});

Notes and Edge Cases

  1. When an empty array is passed as an argument, the returned Promise will remain pending indefinitely.
  2. If a non-iterable object is passed, a TypeError will be thrown.
  3. All other Promises will continue to execute, but their results will not be considered by the Promise returned by race().
// Empty array case
const neverSettled = Promise.race([]);
console.log(neverSettled); // Promise { <pending> }

// Non-iterable object
try {
  Promise.race({});
} catch (e) {
  console.error(e instanceof TypeError); // true
}

// Other Promises continue execution
const p1 = new Promise(resolve => setTimeout(() => {
  resolve('p1 resolved');
  console.log('p1 completed');
}, 100));

const p2 = new Promise(resolve => setTimeout(() => {
  resolve('p2 resolved');
  console.log('p2 completed');
}, 200));

Promise.race([p1, p2]).then(console.log); 
// Output:
// p1 resolved
// p1 completed
// p2 completed

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.