通常,我们在需要保证代码在多个异步处理之后执行,会用到:
Promise.all(promises: []).then(
// 后续逻辑
);
Promise.all可以保证,promises数组中所有promise对象都达到resolve状态,才执行then回调。
如果你的promises数组中每个对象都是http请求,且数量巨大,那么会出现的情况是,你在瞬间发出大量的http请求(tcp连接数不足可能造成等待),或者堆积了大量调用栈导致内存溢出。所以需要对Promise.all做并发限制。(例如微信小程序wx.request并发数超过10就会报错)
因为promise中的操作,在实例化promise对象的时候就执行了,所以只要控制promise实例化的逻辑即可。
参考
async-pool,asyncPool提供了两种实现,一种是基于 ES6 标准的Promise,另外一种是利用 ES7 的async函数来实现
- es6
let result = []; // 待执行的promise数组
console.log('开始时间:',new Date());
for (let i = 0; i < 10; i++) {
result.push(promiseFn(i));
}
PromiseLimit(result).then(res => {
console.log('PromiseLimit成功结束时间:',new Date());
}).catch(er => {
console.log('PromiseLimit失败结束时间:',new Date());
})
// 实例化promise函数
function promiseFn(index) {
return function() {
return new Promise((resolve, reject) => {
let time = parseInt(Math.random() * 10)*1000;
setTimeout(() => {
console.log('第'+index+'个异步动作','耗时'+time);
resolve(true);
}, time);
});
}
}
// 并发限制
function PromiseLimit(funcArray, limit = 5) {
let i = 0;
const result = []; // promise数组
const executing = []; // 正在执行队列
const queue = function() {
// 边界处理, funcArray 为空或者 promise 都已达到 resolve 状态
if (i === funcArray.length) return Promise.all(executing);
// 自调用函数,初始化一个 promise
const p = funcArray[i++]();
// 放入 promise 数组中
result.push(p);
// promise 执行完毕,从正在执行队列中删除
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
// 将正在执行的 promise 插入到 executing 数组
executing.push(e);
// 使用Promise.rece,每当executing数组中最早执行结束的promise完成后,实例化新的promise并执行
if (executing.length >= limit) {
return Promise.race(executing).then(
() => queue(),
e => Promise.reject(e)
);
}
// 递归,直至遍历完funcArray
return Promise.resolve().then(() => queue());
};
return queue().then(() => Promise.all(result));
}
执行顺序:
从 funcArray 第 1 个元素开始,初始化 promise 对象,同时用一个 executing 数组保存正在执行的 promise
不断初始化 promise,直到达到 limit
使用 Promise.race,获得 executing 中 promise 的执行情况,当有一个 promise 执行完毕,继续初始化 promise 并放入 executing 中
所有 promise 都执行完了,调用 Promise.all 返回。
打印结果如图,若正常Promise.all同时执行,耗时应为10个任务中最长的那个,为9s,实现并发后,并发限制数量为5,所以该10个异步操作总耗费时间为16s。