最近领导给了一个需求——大文件上传,由于文件过大(几百MB),故而采取了分片上传机制,将大文件分割成了多个小分片进行上传;此时出现了这样一个问题:
- 浏览器支持并发请求,谷歌浏览器貌似是从59版本开始,支持并发请求数为 6;
- 大文件共分了100片,待请求资源就有100个,依次排队,它们在一定时间内会占用全部的并发请求数,导致其他请求无法下发。
这时,就需要控制分片上传请求的并发数不能占满 6 个,影响其他请求下发;这个问题可以转化成实现一个并发请求函数concurrencyRequest(urls, maxNum)
,要求如下:
- 最大并发数
maxNum
= 3;- 每当有一个请求返回,就留下一个空位,可以增加新的请求;
- 所有请求完成后,结果按照
urls
顺序依次打印出来,返回一个results
集合;
需要考虑的特殊情况:
urls
的长度为0
,results
为空数组;maxNum
大于urls
的长度,应该取的是urls
的长度;- 需要定义一个
count
计数器来判断是否已全部请求完成; - 由于没有考虑请求是否请求成功,所以请求成功或报错都应该把结果保存在
results
集合中; results
中的顺序需和urls
中的保持一致;
// 并发请求函数
const concurrencyRequest = (urls, maxNum) => {
return new Promise((resolve) => {
// 特殊情况,urls长度为0
if (urls.length === 0) {
resolve([]);
return;
}
const results = [];
let index = 0; // 下一个请求的下标
let count = 0; // 当前请求完成的数量
// 发送请求,一个请求结束之后才能进行下一个请求
async function request() {
if (index === urls.length) return;
const i = index; // 保存序号,使results和urls相对应
const url = urls[index];
index++;
console.log(`第${i}个 url`, url);
try {
const resp = await fetch(url);
// resp 加入到results
results[i] = resp;
} catch (err) {
// err 加入到results
results[i] = err;
} finally {
count++;
// 判断是否所有的请求都已完成
if (count === urls.length) {
console.log('完成了所有的请求');
resolve(results);
}
request();
}
}
// maxNum和urls.length取最小进行调用
const times = Math.min(maxNum, urls.length);
for(let i = 0; i < times; i++) {
request();
}
})
}
测试代码
const urls = [];
for (let i = 1; i <= 20; i++) {
urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
}
concurrencyRequest(urls, 3).then(res => {
console.log(res);
});