算法 - 手写 Promise.race
1. 题目
实现 promiseRace(iterables),行为对齐原生 Promise.race:
- 接收一个可迭代的 Promise(或普通值)集合,返回一个新 Promise。
- 结果由**第一个 settle(无论 fulfilled 还是 rejected)**的成员决定:它成功就 resolve,它失败就 reject。
- 关键区别于
Promise.all:race 不关心成败,只关心谁先落地。 - 空数组:原生
Promise.race([])会永远 pending(没有任何成员能触发 settle),需照此实现。
示例:
1 | promiseRace([慢成功, 快失败]) -> reject(快的那个先 settle) |
2. 解题思路
一句话:给每个成员同时挂上 resolve 和 reject,谁先调用谁就赢。
因为 Promise 一旦 settle 就冻结,后续再调用 resolve/reject 都是空操作。所以完全不必自己加「是否已完成」的锁:
- 遍历集合,用
Promise.resolve(item)包一层(兼容非 Promise 值,让它也进入微任务队列公平竞争)。 - 对每个包装后的 Promise,
.then(resolve, reject)——把外层的 resolve、reject 直接透传。 - 第一个 settle 的成员会率先调用外层 resolve 或 reject,外层 Promise 随即定型,其余调用被忽略。
空数组直接返回一个不 settle 的 Promise(
new Promise(() => {}))以匹配原生语义。时间复杂度:
O(n)建立回调。
3. TypeScript 实现
1 | function promiseRace<T>( |
4. 面试延伸
- race vs any:
Promise.any只在第一个成功时定胜负,忽略失败;只有全部失败才 reject 一个AggregateError。别把 race 的「先 settle」和 any 的「先成功」搞混。 - race vs allSettled:allSettled 等所有人落地并汇总状态,永不 reject;race 恰恰相反,抢跑即定。
- 实战用法:请求超时控制是 race 最典型场景——
Promise.race([fetchData(), timeout(5000)]),超时的那个先 reject 就达成「N 秒没返回就报错」。 - 能主动说出「空数组永远 pending」这个反直觉细节,是加分信号,说明你真用过而非只背签名。
难度:中等 | 手写 Promise 家族 | 超时控制基石