一、介绍
MDN Web docs 官方介绍:Promise.race()
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.
示例:
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));
Promise.any([promise1, promise2, promise3]).then((value) => console.log(value));
// Expected output: "quick"
二、注意事项
手动实现一个 Promise.race()
需要注意以下几点:
-
参数验证:需要确保传入的参数是一个可迭代的对象,例如数组或类数组对象。
-
处理空数组情况:如果传入的数组为空,则返回一个永远不会解决的 Promise 对象,因为没有任何 Promise 可以竞速。
-
处理非 Promise 对象:需要确保传入的数组中的每个元素都是 Promise 对象,如果有任何一个元素不是 Promise 对象,则需要将其包装为 Promise 对象。
-
等待第一个 Promise 解决或拒绝:需要等待传入的所有 Promise 对象中的第一个解决或拒绝。为此,可以使用一个循环来遍历传入的 Promise 数组,并在第一个 Promise 解决或拒绝后解决返回的 Promise 对象。
-
处理拒绝情况:如果传入的第一个 Promise 对象拒绝(即状态变为 rejected),则返回的 Promise 对象也应该立即拒绝,并且使用第一个拒绝的 Promise 的拒绝原因作为自己的拒绝原因。
-
处理多次调用:一旦有一个 Promise 对象解决或拒绝,返回的 Promise 对象应该立即解决或拒绝,并且忽略后续的 Promise 对象。
-
错误处理:需要捕获可能发生的错误,并将其作为拒绝原因传递给返回的 Promise 对象。
综上所述,手动实现一个 Promise.race()
需要考虑参数验证、空数组情况、Promise 对象的包装、等待第一个 Promise 解决或拒绝、处理拒绝情况、处理多次调用以及错误处理等方面的问题。
三、实现
function isPromise(obj) {
return !!obj &&
(typeof obj === 'function' || typeof obj === 'object') &&
typeof obj.then === 'function'
}
function myPromiseRace(promises) {
// 参数验证
if (!Array.isArray(promises)) {
return Promise.reject(new TypeError('参数必须是一个可迭代的对象'));
}
// 处理空数组情况
if (promises.length === 0) {
return Promise.resolve();
}
// 返回一个新的 Promise 对象
return new Promise((resolve, reject) => {
// 遍历传入的 Promise 数组
for (const promise of promises) {
// 处理非 Promise 对象
//if (!(promise instanceof Promise)) {
if (!isPromise(promise)) {
promise = Promise.resolve(promise);
}
// 等待第一个 Promise 解决或拒绝
promise.then(resolve).catch(reject);
}
});
}
// 测试
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise 1 resolved'), 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Promise 2 rejected')), 500);
});
myPromiseRace([promise1, promise2])
.then((value) => console.log('Race winner:', value))
.catch((error) => console.error('Race error:', error));