使用 promise.race 实现异步请求超时控制的核心做法是:将实际的业务 promise 与一个设定了延迟的超时 promise 放入 promise.race 中竞争,一旦超时 promise 先完成(抛出错误),整个竞态就会立即终止并进入异常处理分支。具体实现时,需要创建一个在指定毫秒数后通过 settimeout 触发 reject 的 promise,并将其与实际请求 promise 一起作为数组参数传入 race 方法。

如何构建可复用的超时包装函数
为了在实际开发中方便地复用这一逻辑,通常会将上述过程封装为一个高阶函数或工具方法。该函数接收原始 Promise、超时时间(毫秒)以及可选的错误信息作为参数。当调用该函数时,它会返回一个新的 Promise,这个新 Promise 的行为完全取决于 Promise.race 的结果:如果业务请求在超时前完成,则返回业务结果;如果超时发生,则返回被拒绝的状态,从而让调用者可以通过 .catch() 统一处理超时异常。

function withTimeout(promise, ms, errorMsg = 'Request timed out') {
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error(errorMsg)), ms);
});
return Promise.race([promise, timeout]);
}
如何处理并发请求中的部分超时
在处理多个并发请求时,Promise.race 的特性会导致“一损俱损”的效果,即只要有一个 Promise 解决或拒绝,整个集合就结束。因此,若需对每个请求独立应用超时控制,必须为每个请求单独包裹一层超时逻辑。这样即使某个请求超时被拒绝,其他正常的请求仍能继续执行并最终通过 Promise.allSettled 收集所有结果,确保单个请求的超时不会意外中断其他健康的请求流程。

const requests = [
fetch('/api/data1').then(res => res.json()),
fetch('/api/data2').then(res => res.json())
];
const timedRequests = requests.map(req => withTimeout(req, 5000));
Promise.allSettled(timedRequests).then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Success:', result.value);
} else {
console.error('Failed:', result.reason.message);
}
});
});
超时发生时 Promise.race 内部状态是如何变化的
当 Promise.race 检测到超时 Promise 率先 resolve 或 reject 时,它会立即将自身的状态同步为该 Promise 的状态,并丢弃尚未完成的业务 Promise。此时,业务请求虽然在后台可能仍在运行,但前端已经不再等待其结果,而是直接跳入错误处理逻辑。这种机制确保了资源不会被无谓地长时间占用,同时也避免了因网络延迟导致的界面假死现象。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











