Promise.race是否在首个Promise resolve后忽略其余Promise?

核心结论
Promise.race的行为逻辑是:只要传入的所有Promise中,有任意一个率先敲定(无论是resolve成功还是reject失败),Promise.race就会立刻将自身状态同步为这个率先敲定的Promise的状态,其余Promise后续的状态变更不会再对race的返回值产生任何影响,但这些Promise本身不会被暂停或终止,会继续在后台执行直到结束。
你观察到的"无论responsePromise状态如何,timeoutPromise都会执行resolve"是完全符合JS运行逻辑的正常现象,和Promise.race的机制不冲突:
- 你在构造timeoutPromise的时候,内部的
setTimeout回调是同步注册到JS事件队列的,注册动作发生在你调用Promise.race的瞬间,不会因为responsePromise先resolve就自动撤销这个定时器。 - 如果responsePromise在250ms内率先resolve,race会立刻返回请求结果,但已经注册的定时器不会自动清除,到250ms节点时依然会执行timeoutPromise的resolve逻辑,只是这时候timeoutPromise的结果已经不会被race采纳,相当于空跑一次resolve,不会影响业务逻辑。
- 如果responsePromise耗时超过250ms,timeoutPromise率先resolve,race就会返回你预设的超时结果,后续responsePromise就算完成也不会被race接收。
代码修正方案
如果你的需求是避免无意义的定时器空跑,或者要在超时后明确抛出错误、中断请求,可以手动增加资源清理逻辑,参考实现:
// 给请求加超时的通用封装 function requestWithTimeout(responsePromise, timeout = 250) { let timer = null // 超时逻辑建议用reject,方便和正常请求结果做区分 const timeoutPromise = new Promise((_, reject) => { timer = setTimeout(() => { reject(new Error(`请求超时,超过${timeout}ms未响应`)) }, timeout) }) return Promise.race([ // 请求不管成功失败,只要先敲定就清掉定时器,避免空跑 responsePromise.finally(() => clearTimeout(timer)), timeoutPromise ]) }
如果你的请求原生支持AbortController中断信号,还可以在超时触发时主动调用abort方法终止正在进行的请求,彻底避免无效的网络请求消耗资源。
内容的提问来源于stack exchange,提问作者neer2005
相关产品推荐
相关产品推荐

