在网页开发中,我们经常需要从网络上加载JavaScript脚本(比如一些工具库),就像从网上下载一个工具来帮我们完成任务。但有时候网络不稳定,或者下载的地方(比如CDN服务器)出了问题,脚本就加载不下来,网页可能会出故障。脚本加载容灾方案就像一个备用计划,确保即使网络有问题,网页也能正常工作。
1. 基础重试方案:加载失败就再试几次
假设你在网上买东西,快递丢了,你会再下单试试,对吧?脚本加载也是类似的想法:如果加载失败,我们可以自动再试几次。下面是一个简单的实现:
// 定义一个加载脚本的函数,支持重试功能
function loadScript(url, retry = 3, delay = 1000) {
return new Promise((resolve, reject) => {
const script = document.createElement('script'); // 创建一个<script>标签,就像在网页里加一个新工具
script.src = url; // 设置脚本的地址(从哪里下载)
script.onload = () => resolve({ url, success: true }); // 加载成功后,告诉我们“任务完成”
script.onerror = () => { // 加载失败时触发这个函数
if (retry > 0) { // 如果还有重试机会(比如还剩3次、2次)
setTimeout(() => { // 等待一段时间再试,避免太快重复请求
console.log(`[再试一次] 还剩${retry}次机会`);
loadScript(url, retry - 1, delay * 2) // 再次调用自己,重试次数减1,等待时间翻倍
.then(resolve) // 如果成功,告诉我们
.catch(reject); // 如果失败,继续报告错误
}, delay); // 比如第一次等1秒,第二次等2秒
} else {
reject(new Error(`彻底失败了: ${url}`)); // 重试次数用完,宣布失败
}
};
document.head.appendChild(script); // 把<script>标签加到网页头部,开始加载
});
}
// 使用这个函数加载一个脚本
loadScript('https://cdn.example.com/sdk.js') // 尝试加载一个工具库
.then(() => console.log('加载成功啦!')) // 成功时的提示
.catch(() => console.log('加载失败,可能是网络问题')); // 失败时的提示
代码注释说明:
- Promise:就像一个承诺,告诉你任务是成功还是失败。
- document.createElement('script'):在网页里动态创建一个脚本标签。
- retry:重试次数,默认是3次。
- delay:每次失败后等待的时间,默认1秒,后面会变长(1秒、2秒、4秒)。
- setTimeout:让程序“休息”一会儿再试。
这个方案怎么工作:
- 如果脚本第一次加载失败,等1秒再试。
- 第二次失败,等2秒再试。
- 第三次失败,等4秒再试。
- 如果3次都失败,就放弃,提示错误。
2. 进阶方案:更聪明的重试办法
基础方案已经不错了,但如果很多人都同时重试,服务器可能会忙不过来。我们可以用指数退避算法,让每次重试的等待时间变长,还加点随机时间,避免大家都挤在一起请求。
// 定义一个更聪明的重试函数
async function smartRetry(url, options = {}) {
const { maxRetries = 5, baseDelay = 1000, timeout = 8000 } = options; // 设置默认值:最多试5次,基础等待1秒,超时8秒
for (let i = 0; i <= maxRetries; i++) { // 循环尝试,最多5次
try {
await Promise.race([ // 让两个任务“赛跑”,谁先完成算谁的
loadScript(url), // 尝试加载脚本
new Promise((_, reject) => setTimeout(() => reject(new Error('超时啦')), timeout)) // 如果8秒还没加载好,就放弃
]);
console.log('脚本加载成功!'); // 成功了,开心!
return true; // 结束任务
} catch (err) {
if (i === maxRetries) throw err; // 如果这是最后一次尝试还失败,就彻底放弃
const delay = baseDelay * (2 ** i) + Math.random() * 500; // 计算下次等待时间,比如1秒、2秒、4秒,再加点随机(0-0.5秒)
console.log(`失败了,等${delay/1000}秒再试...`); // 告诉我们下次啥时候再试
await new Promise(resolve => setTimeout(resolve, delay)); // 休息一会儿
}
}
}
// 使用这个聪明的方法
smartRetry('https://unpkg.com/vue@3.2.37/dist/vue.global.prod.js') // 加载一个Vue.js库
.catch(err => console.error('所有尝试都失败了:', err)); // 如果全失败了,打印错误
代码注释说明:
- async/await:让异步代码看起来像同步的,更好懂。
- Promise.race:就像比赛,谁先到终点(成功或超时)就用谁的结果。
- 2 ** i:每次等待时间翻倍,比如1秒、2秒、4秒。
- Math.random() * 500:加点随机时间(0-0.5秒),避免大家都同时重试。
这个方案的好处:
- 不会让服务器太忙。
- 不会等太久,8秒没成功就换下一个办法。
3. 备用方案:换个地方下载
如果一个地方(比如某个CDN)总是加载失败,我们可以准备几个备用地址,甚至用本地的备份文件。下面是一个例子:
// 定义几个可用的脚本地址
const CDN_URLS = [
'https://cdn1.example.net/sdk.js', // 第一个CDN
'https://cdn2.example.com/sdk.js', // 第二个CDN
'/local-backup/sdk.js' // 本地备份文件
];
// 尝试从多个地址加载脚本
async function multiCDNLoader(urls) {
const errors = []; // 用来记录哪里出了问题
for (const url of urls) { // 挨个试试这些地址
try {
await loadScript(url); // 尝试加载这个地址的脚本
console.log(`成功从${url}加载!`); // 成功了,告诉我们从哪里加载的
return; // 加载成功就结束
} catch (err) {
errors.push({ url, err }); // 如果失败,记下错误,继续试下一个
console.log(`${url}失败了,试下一个...`);
}
}
throw new Error('所有地址都失败了!'); // 如果全都失败,宣布彻底没戏
}
// 使用这个函数
multiCDNLoader(CDN_URLS)
.catch(err => console.error('加载失败了:', err)); // 如果全失败,打印错误
代码注释说明:
- CDN_URLS:一个地址列表,包含多个备用方案。
- for...of:挨个试这些地址。
- errors.push:把失败的信息存起来,方便排查问题。
这个方案的好处:
- 多准备几个“商店”,一个不行就换下一个。
- 最后还有本地备份,特别靠谱。
4. 小结
- 简单开始:先试试基础重试方案,很好上手。
- 逐步升级:等你熟悉了,可以用聪明重试或多个CDN。
- 实践一下:找个小项目试试这些代码,看看效果。
8406

被折叠的 条评论
为什么被折叠?



