Promise.race()

Promise.race() 静态方法采用可迭代的 Promise 作为输入并返回单个 Promise。这个返回的 Promise 将随着第一个 Promise 的最终状态而确定。

¥The Promise.race() static method takes an iterable of promises as input and returns a single Promise. This returned promise settles with the eventual state of the first promise that settles.

Try it

语法

¥Syntax

js
Promise.race(iterable)

参数

¥Parameters

iterable

iterable(例如 Array)的 Promise。

返回值

¥Return value

iterable 中第一个 Promise 的最终状态异步结算的 Promise。换句话说,如果第一个解决的承诺得到履行,它就实现,如果第一个解决的承诺被拒绝,它就拒绝。如果传递的 iterable 为空,则返回的 Promise 将永远保持未决状态。如果传递的 iterable 非空但不包含待处理的 Promise,则返回的 Promise 仍然是异步(而不是同步)结算的。

¥A Promise that asynchronously settles with the eventual state of the first promise in the iterable to settle. In other words, it fulfills if the first promise to settle is fulfilled, and rejects if the first promise to settle is rejected. The returned promise remains pending forever if the iterable passed is empty. If the iterable passed is non-empty but contains no pending promises, the returned promise is still asynchronously (instead of synchronously) settled.

描述

¥Description

Promise.race() 方法是 承诺并发 方法之一。当你希望第一个异步任务完成,但不关心其最终状态(即它可以成功或失败)时,它非常有用。

¥The Promise.race() method is one of the promise concurrency methods. It's useful when you want the first async task to complete, but do not care about its eventual state (i.e. it can either succeed or fail).

如果可迭代包含一个或多个非承诺值和/或已解决的承诺,则 Promise.race() 将解决为可迭代中找到的第一个值。

¥If the iterable contains one or more non-promise values and/or an already settled promise, then Promise.race() will settle to the first of these values found in the iterable.

示例

¥Examples

使用 Promise.race()

¥Using Promise.race()

此示例展示了如何使用 Promise.race() 来竞争使用 setTimeout() 实现的多个计时器。时间最短的计时器总是赢得比赛并成为最终承诺的状态。

¥This example shows how Promise.race() can be used to race several timers implemented with setTimeout(). The timer with the shortest time always wins the race and becomes the resulting promise's state.

js
function sleep(time, value, state) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (state === "fulfill") {
        return resolve(value);
      } else {
        return reject(new Error(value));
      }
    }, time);
  });
}

const p1 = sleep(500, "one", "fulfill");
const p2 = sleep(100, "two", "fulfill");

Promise.race([p1, p2]).then((value) => {
  console.log(value); // "two"
  // Both fulfill, but p2 is faster
});

const p3 = sleep(100, "three", "fulfill");
const p4 = sleep(500, "four", "reject");

Promise.race([p3, p4]).then(
  (value) => {
    console.log(value); // "three"
    // p3 is faster, so it fulfills
  },
  (error) => {
    // Not called
  },
);

const p5 = sleep(500, "five", "fulfill");
const p6 = sleep(100, "six", "reject");

Promise.race([p5, p6]).then(
  (value) => {
    // Not called
  },
  (error) => {
    console.error(error.message); // "six"
    // p6 is faster, so it rejects
  },
);

Promise.race 的异步性

¥Asynchronicity of Promise.race

以下示例演示了 Promise.race 的异步性。与其他 Promise 并发方法不同,Promise.race 始终是异步的:即使 iterable 为空,它也不会同步稳定。

¥This following example demonstrates the asynchronicity of Promise.race. Unlike other promise concurrency methods, Promise.race is always asynchronous: it never settles synchronously, even when the iterable is empty.

js
// Passing an array of promises that are already resolved,
// to trigger Promise.race as soon as possible
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];

const p = Promise.race(resolvedPromisesArray);
// Immediately logging the value of p
console.log(p);

// Using setTimeout, we can execute code after the stack is empty
setTimeout(() => {
  console.log("the stack is now empty");
  console.log(p);
});

// Logs, in order:
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: 33 }

空的迭代会导致返回的承诺永远处于待处理状态:

¥An empty iterable causes the returned promise to be forever pending:

js
const foreverPendingPromise = Promise.race([]);
console.log(foreverPendingPromise);
setTimeout(() => {
  console.log("the stack is now empty");
  console.log(foreverPendingPromise);
});

// Logs, in order:
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "pending" }

如果可迭代包含一个或多个非承诺值和/或已确定的承诺,则 Promise.race 将确定为数组中找到的第一个值:

¥If the iterable contains one or more non-promise value and/or an already settled promise, then Promise.race will settle to the first of these values found in the array:

js
const foreverPendingPromise = Promise.race([]);
const alreadyFulfilledProm = Promise.resolve(100);

const arr = [foreverPendingPromise, alreadyFulfilledProm, "non-Promise value"];
const arr2 = [foreverPendingPromise, "non-Promise value", Promise.resolve(100)];
const p = Promise.race(arr);
const p2 = Promise.race(arr2);

console.log(p);
console.log(p2);
setTimeout(() => {
  console.log("the stack is now empty");
  console.log(p);
  console.log(p2);
});

// Logs, in order:
// Promise { <state>: "pending" }
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: 100 }
// Promise { <state>: "fulfilled", <value>: "non-Promise value" }

使用 Promise.race()实现请求超时

¥Using Promise.race() to implement request timeout

你可以使用拒绝的计时器来竞争可能持久的请求,这样当时间限制过去时,生成的承诺会自动拒绝。

¥You can race a potentially long-lasting request with a timer that rejects, so that when the time limit has elapsed, the resulting promise automatically rejects.

js
const data = Promise.race([
  fetch("/api"),
  new Promise((resolve, reject) => {
    // Reject after 5 seconds
    setTimeout(() => reject(new Error("Request timed out")), 5000);
  }),
])
  .then((res) => res.json())
  .catch((err) => displayError(err));

如果 data 承诺履行,它将包含从 /api 获取的数据;否则,如果 fetch 保持等待 5 秒,它将拒绝并输掉与 setTimeout 计时器的比赛。

¥If the data promise fulfills, it will contain the data fetched from /api; otherwise, it will reject if fetch remains pending for 5 seconds and loses the race with the setTimeout timer.

使用 Promise.race() 检测 Promise 的状态

¥Using Promise.race() to detect the status of a promise

因为 Promise.race() 解析为可迭代中的第一个非挂起的 Promise,所以我们可以检查 Promise 的状态,包括它是否处于挂起状态。本示例改编自 promise-status-async

¥Because Promise.race() resolves to the first non-pending promise in the iterable, we can check a promise's state, including if it's pending. This example is adapted from promise-status-async.

js
function promiseState(promise) {
  const pendingState = { status: "pending" };

  return Promise.race([promise, pendingState]).then(
    (value) =>
      value === pendingState ? value : { status: "fulfilled", value },
    (reason) => ({ status: "rejected", reason }),
  );
}

在此函数中,如果 promise 处于待决状态,则第二个值 pendingState(非承诺)将成为竞赛结果;否则,如果 promise 已经解决,我们可以通过 onFulfilledonRejected 处理程序知道它的状态。例如:

¥In this function, if promise is pending, the second value, pendingState, which is a non-promise, becomes the result of the race; otherwise, if promise is already settled, we may know its state through the onFulfilled and onRejected handlers. For example:

js
const p1 = new Promise((res) => setTimeout(() => res(100), 100));
const p2 = new Promise((res) => setTimeout(() => res(200), 200));
const p3 = new Promise((res, rej) => setTimeout(() => rej(300), 100));

async function getStates() {
  console.log(await promiseState(p1));
  console.log(await promiseState(p2));
  console.log(await promiseState(p3));
}

console.log("Immediately after initiation:");
getStates();
setTimeout(() => {
  console.log("After waiting for 100ms:");
  getStates();
}, 100);

// Logs:
// Immediately after initiation:
// { status: 'pending' }
// { status: 'pending' }
// { status: 'pending' }
// After waiting for 100ms:
// { status: 'fulfilled', value: 100 }
// { status: 'pending' }
// { status: 'rejected', reason: 300 }

注意:promiseState 函数仍然异步运行,因为即使已经解决,也无法同步获取 Promise 的值(即没有 then()await)。然而,promiseState() 总是在一个周期内完成,并且从不真正等待任何承诺的结算。

¥Note: The promiseState function still runs asynchronously, because there is no way to synchronously get a promise's value (i.e. without then() or await), even when it is already settled. However, promiseState() always fulfills within one tick and never actually waits for any promise's settlement.

与 Promise.any() 的比较

¥Comparison with Promise.any()

Promise.race 取得第一个结算的 Promise

¥Promise.race takes the first settled Promise.

js
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "one");
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, "two");
});

Promise.race([promise1, promise2])
  .then((value) => {
    console.log("succeeded with value:", value);
  })
  .catch((reason) => {
    // Only promise1 is fulfilled, but promise2 is faster
    console.error("failed with reason:", reason);
  });
// failed with reason: two

Promise.any 取第一个已履行的 Promise

¥Promise.any takes the first fulfilled Promise.

js
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "one");
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, "two");
});

Promise.any([promise1, promise2])
  .then((value) => {
    // Only promise1 is fulfilled, even though promise2 settled sooner
    console.log("succeeded with value:", value);
  })
  .catch((reason) => {
    console.error("failed with reason:", reason);
  });
// succeeded with value: one

规范

Specification
ECMAScript Language Specification
# sec-promise.race

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看