最近准备做大文件上传的业务,就联想到前段时间很火的面试题🤦♂️ 假如有100条请求,前端如何控制并发。因此在网上看了一些优化相关性能的方案,简单记录一下
不做并发
js
async upload(Chunks) {
let queue = [...chunks];
while (i<queue.length) {
//此方法是自定义的上传异步方法不赘述
let uploadRes = await this.uploadFetch(queue[i])
if(uploadRes){
console.log('成功')
}else{
// 可写异常 再重试
//重试优化先不写 queue.push(queue[i])
}
i++;
}
},
局限:
- 资源利用率差,对于文件片段多的时候全部接口成功等待长时间。
promnise.all分组并发
将请求数组均分为多个小数组,每次最多只开启concurrency个并发请求,以此来控制并发数量。每当一组请求完成后再发送新的一批请求,可以实现对异步任务的并发控制。
js
//concurrency 参数为并发数量
**浏览器并发限制是指浏览器对同一域名同时发起的HTTP请求数量的上限**,通常限制在6-8个之间 **
//Chrome/Firefox:默认6个 假如预留一个空闲位的话concurrency设置为5
async concurrencyUpload(chunks,concurrency) {
let queue = [...chunks];
while (queue.length > 0) {
const currentChunks = queue.splice(0, concurrency);
await Promise.all(
currentChunks.map(async chunk => {
try {
//此方法是自定义的上传异步方法
await this.uploadFetch(chunk);
} catch (err) {
// 重试:将分片重新加入队列
//可优化重试逻辑 控制单任务重试次数
// queue.push(chunk);
}
})
).catch(err => console.error('出错:', err));
}
}
局限:
- 当前批次请求全部完成后才会继续下一批次,可能存在空闲位。
- 可能返回无序的结果。
- 有一个请求失败了,这个
Promise.all就失败了,没有返回值。
使用队列
另一种方式是使用一个队列来手动管理并发。你可以创建一个函数来管理并发请求的发送。
js
class RequestQueue {
constructor(maxConcurrent) {
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.running = 0;
}
enqueue(promiseFn) {
this.queue.push(promiseFn);
this.processQueue();
});
}
processQueue() {
while (this.running < this.maxConcurrent && this.queue.length > 0) {
const promiseFn = this.queue.shift();
this.running++;
promiseFn()
.then(()=>{
//console.log('单任务返回成功')
})
.catch(()=>{
// 重试:将上传任务重新加入队列
//可优化重试逻辑 控制单任务重试次数
this.queue.push(promiseFn)
})
.finally(() => {
this.running--;
this.processQueue(); // Process the next item in the queue
});
}
}
}
// 使用示例
const queue = new RequestQueue(5); // 最大并发5个请求
// 请求函数 uploadFetch 已经是一个promise方法
chunks.forEach(chunk=>{queue.enqueue(this.uploadFetch(chunk))})
用第三方库 p-limit
安装依赖
js
npm install p-limit
js实现
js
import pLimit form 'p-limit';
const limit = pLimit(2); // 限制并发数为2
const results = await Promise.all(chunks.map(chunk => limit(() => this.uploadFetch(chunk))));