promise控制并发
排队的办法
假如去饭堂打菜,只有10个窗口,但学生有上百人,应该怎么办呢?假设学生打菜是IO操作时间不等,我们可以用递归等上一个人做完再去做下一个人.我一开始想到的办法是reduce.
let arr =[1,2,3,4,5,6,7,8,9,10] //假设有10个学生
arr.reduce((last,now,index)=>{
last.then(()=>{
setTimout(()=>{
resolve(now)
},Math,random()*1000)
})
},Promise.resolve())
这种办法可以控制上一个学生打完菜才到下一个学生,但这不符合要求,我们是有三个窗口,上面的情况相当于只有一个窗口.因此,我们应该自己实现一个递归,在递归里面判断当前的任务数量.
function delay(){
//模拟异步操作
return new Promise((resolve,reject)=>{
setTimeout(function(){
resolve()
},Math.random()*10000)
})
}
function recusive(tasks,taskLimit,handler){
let num = 0,index=0;
function a(){
if(num<taskLimit&&index<tasks.length){
Promise.resolve().then(()=>handler(tasks[index++])).then(()=>{
console.log('当前并发数:'+num);
num--;
a();
});
num++;
}
}
//任务初始化分配
for(let i=0;i<taskLimit;i++){
a();
}
}
//测试
recusive(arr,3,(item)=>{ console.log(item); return delay() })
/*
1
2
3
当前并发数:3
4
当前并发数:3
5
当前并发数:3
6
当前并发数:3
7
当前并发数:3
8
当前并发数:3
9
当前并发数:3
10
当前并发数:3
当前并发数:2
当前并发数:1
*/
一开始先同时执行三个任务,有一个退出以后就接替下一个,数量一直为3
async await的方法
async await是promise的语法糖,在async 函数里面的await能够堵塞代码等待代码执行完毕后才进行下一步,所以可以替换then来保持队列的长度一直为常数值.
function recusive(tasks,taskLimit,handle){
let num=0,index=0,lock=[];
function block(){
//堵塞
return new Promise((resolve,reject)=>{
console.log('堵塞')
lock.push(resolve)
})
}
function release(){
//释放
console.log('释放')
lock.length&&lock.shift()()
}
async function a(){
if(num>=taskLimit){
await block();
}
if(index<tasks.length){
num++;
console.log('当前并发数:'+num);
await handle(tasks[index++]);
release();
num--;
}
}
for(let i=0;i<tasks.length;i++)a();
}
promise.race的办法
promise.race可以返回promise数组中第一个执行完成的promise对象的结果,只要填满它race的时候再添加新的promise,删除完成的promise就行了.
问题是怎么找到已经完成的promise对象,我们可以在定义的时候把某个属性作为判断,就用永远不会一样的东西最方便,时间就不会相同.
function recusive(tasks, taskLimit, handle) {
let index = 0,
num = 0,
promisesArr = [];
function a(tasks, taskLimit, handle) {
console.log('任务数量:' + promisesArr.length)
let id, p;
if (index <= tasks.length) {
if (promisesArr.length < taskLimit) {
id = new Date().getTime() //唯一标识符
p = new Promise((resolve, reject) => { //p 之所以放在里面是不想先执行了p
console.log(`${index}开始执行`)
handle(tasks[index++])
.then(() => {
resolve(id)
})
})
p.id = id
p.then(id => {
promisesArr=promisesArr.filter(item => item.id != id)
})
num++;
promisesArr.push(p)
a(tasks, taskLimit, handle)
} else {
// 超过并发数 用race去掉旧的换新的
Promise.race(promisesArr).then(() => {
id = new Date().getTime() //唯一标识符
p = new Promise((resolve, reject) => {
console.log(`${index}开始执行`)
handle(tasks[index++])
.then(() => {
resolve(id)
})
})
p.id = id
promisesArr.push(p)
return p.then(id => {
promisesArr=promisesArr.filter(item => item.id != id)
})
}).then(()=>{
a(tasks, taskLimit, handle)
})
}
}
}
for (let i = 0; i <3; i++) {
a(tasks, taskLimit, handle)
}
}
let arr = Array.from({
length: 100
}, (item, i) => i)
//测试用例
recusive(arr, 3, (item) => new Promise((resolve, reject) => {
setTimeout(() => {
console.log(item);
resolve(item)
}, Math.random() * 10000)
}))