面试官问:如果有100个请求,你咋办?
之前看到一篇文章的评论,一位仁兄说:“谁会傻不拉几的一次性发起 100 多条请求的?”
一看这位仁兄就是对大文件的分片请求没有任何概念。
如果有一个1.5G
的文件,不管是上传服务器,还是下载到本地,如果不分片,请问这位仁兄你咋办?难道你要一次性1.5G
把它请下来?
其实大文件请求很多人都用过,为什么不知道内部怎么搞的呢?主要问题就是咱们做的项目都是成熟项目,只需要调用相应的接口,运用相应的方法就成功了,完全没有去刨根问底,所以你觉得即使再大的文件,也是一次性请求下来。
之前在大文件分块上传的文章里面已经用过异步并发了
现在再带大家专门研究下这个东西。
异步并发数的代码原理有两种:
一个是:执行队列里面的异步请求数达到6的时候,把他们放到Promise.all() 或者 Promise.allSettled()里面一起并发执行,执行完以后清空数组,继续往里面加六个,直到最后结束。它的精髓是用await去暂停while等拿到这6个结果以后,再去清空数组。
另一个是:在执行数组里面加入六个请求,只要有一个请求获得了结果,那就把他从数组里面清除掉,然后再加入一个新的异步请求进来。它主要利用的是函数的递归处理。
还有一个就是第三方库,不管第三方库再怎么好用,不能因为有他们的存在,我们就不用去探索它的封装原理,毕竟那是他们的理解,不是我们的,我们要做的是,在他们的基础上,封装出更好的,更完美的,更适合自己的库出来。
下面就对这 2 种异步并发代码做简单的书写:
第一种是:
Promise.allSettled()
每六个六个的发送请求,但是它的弊端是:如果有一个请求阻塞了,那这一组就要等着,直到它拿到响应数据为止。
// 每3个执行完以后,清空队列,继续加3个,直到所有的请求全部执行完
const max = 6; // 并发请求数量
let taskPool = []; // 请求队列
const total = 12; // 一共有17个异步请求
let index = 0;
// 异步请求
const asyncFn = (index)=>{
return new Promise((resolve)=>{
setTimeout(()=>{
console.log("定时")
resolve(`第 ${index} 个成功了!`)
}, 500)
})
}
async function fn(){
while(index < total){
if(taskPool.length < max){
taskPool.push(asyncFn(index))
}
if(taskPool.length === max) {
await Promise.allSettled(taskPool).then((res)=>{
console.log(res);
taskPool=[]
})
}
index ++;
}
}
fn()
上面代码有个逻辑bug,大家可以动动手找处理,补好,记得和我分享下你的解决方案呀!测试如下:
第二种是: 利用递归法
其实很多时候我们想要做的是:请求池里面有6个请求,然后谁执行结束以后,就去执行递归函数,再往数组里面加新的请求。
const arr = [];
for (let i = 0; i < 100; i++){
arr.push(() => new Promise((resolve) => {
setTimeout(() => {
console.log('done', i);
resolve();
}, 100 * i);
}));
};
const run = (arr, max) => {
const runingTask = []; //请求池
// 当请求总数小于最大请求数,全部加入,然后循环执行。
while (runingTask.length < max && arr.length) {
// 利用循环,先将第前6个加入进来
runingTask.push(arr.shift());
}
// 当请求总数大于最大请求数
while (runingTask.length) {
runingTask.shift()().finally(() => {
run(arr, max);
});
}
};
run(arr, 6);
执行结果如下:
它的数据一个一个出来的。
第三种是: 第三方库:
此外,还有一些第三方库可以使用,例如async.js
和p-limit
等。p-limit
是一个专门用于控制Promise并发的小型库
。可以在p-limit
文档中找到更多信息和示例。
转载自:https://juejin.cn/post/7393525123766140943