JS实现带并发的异步任务调度器-promise
问题描述
实现一个具有并发数量限制的异步任务调度器,可以规定最大同时运行的任务。
JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善下面代码的Scheduler类,使以下程序能够正常输出:
class Scheduler {
add(promiseCreator) { ... }
// ...
}
const timeout = time => new Promise(resolve => {
setTimeout(resolve, time);
})
const scheduler = new Scheduler(2);
const addTask = (time,order) => {
scheduler.add(() => timeout(time)).then(()=>console.log(order))
//scheduler.add(() => timeout(time)) 参数是一个promise,返回一个promise
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// output: 2 3 1 4
整个的完整执行流程:
起始1、2两个任务开始执行
500ms时,2任务执行完毕,输出2,任务3开始执行
800ms时,3任务执行完毕,输出3,任务4开始执行
1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行
1200ms时,4任务执行完毕,输出4
分析
任务调度器-控制任务的执行,当资源不足时将任务加入等待队列,当资源足够时,将等待队列中的任务取出执行
在调度器中一般会有一个等待队列queue,存放当资源不够时等待执行的任务。
具有并发数据限制,假设通过max设置允许同时运行的任务,还需要count表示当前正在执行的任务数量。
当需要执行一个任务A时,先判断count==max
如果相等说明任务A不能执行,应该被阻塞,阻塞的任务放进queue中,等待任务调度器管理。
如果count<max
说明正在执行的任务数没有达到最大容量,那么count++
执行任务A,执行完毕后count--
。
此时如果queue中有值,说明之前有任务因为并发数量限制而被阻塞,现在count<max
,任务调度器会将对头的任务弹出执行。
实现
阻塞的方法:采用new一个Promise对象,将resolve函数的引用推入队列queue中。只要resolve函数没有被执行,当前任务就会一直阻塞在这里。
class Scheduler {
constructor(max){
this.max = max;
this.count = 0 ;//用来记录当前正在执行的异步函数
this.queue = new Array();//表示等待队列
}
async add(promiseCreator){
/*
此时count已经满了,不能执行本次add需要阻塞在这里,将resolve放入队列中等待唤醒,
等到count<max时,从队列中取出执行resolve,执行,await执行完毕,本次add继续
*/
if(this.count>=this.max) await new Promise((resolve,reject)=>this.queue.push(resolve))
this.count ++;
let res = await promiseCreator();
this.count--;
if(this.queue.length){//依次唤醒add
// 若队列中有值,将其resolve弹出,并执行
// 以便阻塞的任务,可以正常执行
this.queue.shift()();
}
return res;
}
}
const timeout = time => new Promise(resolve => {
setTimeout(resolve, time);
})
const scheduler = new Scheduler(2);
const addTask = (time,order) => {
//add返回一个promise,参数也是一个promise
scheduler.add(() => timeout(time)).then(()=>console.log(order))
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// output: 2 3 1 4