使用promise控制单个终端的并发场景
我们都知道浏览器中
同一域名下,
①同一GET请求的并发数是1,也就是说上一个请求结束,才会执行下一个请求,否则置入队列等待发送;
②不同GET/POST请求的并发数量是6,当发送的请求数量达到6个,并且都没有得到响应时,后面的请求会置入队列等待发送。
如果你的promises数组中每个对象都是http请求,或者说每个对象包含了复杂的调用处理。而这样的对象有几十个。
那么会出现的情况是,你在瞬间发出几十http请求(tcp连接数不足可能造成等待),或者堆积了无数调用栈导致内存溢出。
这时候,我们就需要考虑对Promise.all做并发限制。
Promise.all并发限制思路是,每个时刻并发执行的promise数量是固定的,我们使用Promise.race控制当前有执行完的任务立马启动下一个任务,最终的执行结果的顺序用Promise.all保持与输入的一致。
// 参考async-pool源码
function asyncPool(poolLimit, array, iteratorFn) {
let i=0;
// 缓存所有执行完成的promisepromise对象、最后需要放到promise.all中保证输出的顺序
const ret = [];
// 整在执行中的promise对象,不超过最大的限制
const executing = new Set();
const enqueue = () => {
// 边界处理 空数组或者已经打到最大数量
if (i === array.length) {
return Promise.resolve();
}
// 每调一次enqueue,初始化一个promise
const item = array[i++];
const p = Promise.resolve().then(() => iteratorFn(item, array))
ret.push(p)
executing.add(p);
// 定义一个执行完成之后在executing删除,实时更新executing的数量
const clean = () => executing.delete(p);
p.then(clean).catch(clean);
// 使用Promise.rece,每当executing数组中promise数量低于poolLimit,就实例化新的promise并执行
let r = Promise.resolve();
if (executing.size >= poolLimit) {
r = Promise.race(executing)
}
// 递归,直到遍历完array
return r.then(() => enqueue())
}
return enqueue().then(() => Promise.all(ret));
}
// 使用
asyncPool(10, [/* 40 个请求的参数数据 */], () => {/* 发起请求的函数 */})