
本文详解如何通过 promise.race 实现 fetch 请求的超时控制与备用链路降级,避免阻塞 ui,并确保在主 api 响应超 3 秒时无缝切换至备用接口。
在前端开发中,依赖第三方 API 时常面临网络延迟、服务不可用等问题。单纯用 if 判断无法实现“等待超时后执行另一请求”,因为 fetch() 是异步操作,其耗时不支持同步条件判断(如 if (timeTaken > 3000))。正确方案是利用 Promise.race() —— 它会返回首个 settled(fulfilled 或 rejected)的 Promise,从而天然支持超时竞争机制。
✅ 推荐实现:使用 Promise.race + timeout Promise
// 创建一个在指定毫秒后 reject 的超时 Promise
function timeout(ms, promise) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Request timed out after ${ms}ms`)), ms)
)
]);
}
// 主逻辑:优先请求 link1,3 秒未响应则 fallback 到 link2
const requestOptions = {
method: 'GET',
headers: {
'apikey': 'your-api-key' // 请替换为实际 key
}
};
timeout(3000,
fetch('https://api.apilayer.com/exchangerates_data/latest?base=USD', requestOptions)
)
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(data => {
console.log('✅ Primary API success:', data);
// 处理主数据逻辑(如渲染汇率)
})
.catch(error => {
console.warn('⚠️ Primary API failed or timed out:', error.message);
// 降级请求备用 API
return fetch('https://api.apilayer.com/fixer/latest?base=USD', requestOptions)
.then(res => {
if (!res.ok) throw new Error(`Fallback HTTP ${res.status}`);
return res.json();
})
.then(data => {
console.log('✅ Fallback API success:', data);
// 处理备用数据逻辑
})
.catch(err => {
console.error('❌ Both APIs failed:', err);
// 统一错误处理:提示用户、显示离线状态、启用缓存等
});
});⚠️ 关键注意事项
- 不要滥用 if/else 同步判断:fetch() 返回 Promise,其执行时间无法在调用瞬间获知,if () { fetch(...) } else { fetch(...) } 实际会同时触发两个请求或永远只走 if 分支,完全违背设计意图。
- Promise.race 是核心:它让「主请求」和「超时拒绝」形成竞态,3 秒内完成则取主结果;超时则立即 reject,触发 .catch() 中的降级逻辑。
- 错误需分层处理:主请求可能因网络、4xx/5xx、CORS 等失败,超时只是其中一种场景。建议在 .catch() 中统一判断错误类型,必要时再发起 fallback。
- 避免重复请求:上述代码确保最多只发起 1 次主请求 + 1 次备用请求(仅当主请求失败/超时时),符合性能与资源节约原则。
- 可扩展性建议:如需多级 fallback 或重试策略,可封装为通用函数,例如 fetchWithFallback(url1, url2, options, timeoutMs = 3000)。
通过该模式,你不仅能优雅解决超时降级问题,还能提升应用健壮性与用户体验——这才是现代前端异步容错的正确实践。










