通过promise.race并发加载多cdn资源,首个成功即使用并清理其余请求,支持js/css容灾及超时降级。

通过 Promise 实现多域名静态资源的容灾异步加载,核心是“并发尝试多个 CDN 地址,只要一个成功就立即使用,其余自动中止”,避免单点失败导致资源加载阻塞。
原理:用 Promise.race + 动态 script/link 标签 + 错误清理
关键不是靠轮询或重试,而是同时发起多个加载请求,利用 Promise.race 捕获首个 settled(fulfilled 或 rejected)结果。对 script、CSS 等资源,需手动创建 DOM 元素并监听 load 和 error 事件,将其包装为 Promise。
注意:必须在任一成功后主动移除其他未完成的请求(如 abort fetch、或移除未触发 load 的 <script></script> 标签),防止副作用和内存泄漏。
加载 JS 脚本的容灾封装示例
以下是一个可复用的 loadScript 函数,支持多 URL 并发容灾:
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Script load failed: ${src}`));
document.head.appendChild(script);
});
}
function loadScriptWithFallback(urls) {
const promises = urls.map(url =>
loadScript(url).catch(err => {
// 清理失败的 script 标签(可选,提升健壮性)
const existing = document.querySelector(`script[src="${url}"]`);
if (existing) existing.remove();
throw err;
})
);
return Promise.race(promises);
}
// 使用
loadScriptWithFallback([
'https://cdn-a.example.com/lib.js',
'https://cdn-b.example.com/lib.js',
'https://cdn-c.example.com/lib.js'
])
.then(script => console.log('Loaded:', script.src))
.catch(err => console.error('All fallbacks failed:', err));
加载 CSS 的容灾处理要点
CSS 加载虽不阻塞 JS 执行,但影响渲染。同样可用 Promise.race,但需监听 link 标签的 load 与 error,且建议添加 onload 后校验样式是否实际生效(如检查某 class 是否可被 getComputedStyle 读取),因为某些 CDN 可能返回 200 但内容为空。
- 创建
<link rel="stylesheet">并 append 到 head - 监听
load事件 → resolve - 监听
error事件 → reject 并清理 DOM - 可选:加载后用
document.styleSheets或getComputedStyle验证规则是否注入
进阶:支持超时控制与降级策略
单纯 race 不够稳健——若所有 CDN 延迟高但未报错,可能长时间无响应。建议叠加超时机制:
- 用
Promise.race([Promise.race(promises), timeoutPromise])包裹 - 超时后可触发本地兜底(如内联精简版脚本、或从
/local/lib.js加载) - 记录加载结果(成功域名、耗时、失败原因)用于后续 A/B 路由或监控
例如:首次加载失败后,下次直接跳过该域名;或按历史成功率动态调整 URL 顺序。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











