关于异步并发调度器关键实现:
- 需要有一个数组
queue,模拟队列(先进先出),依次进行异步请求处理。 add方法用来添加异步请求;根据当前正在running的个数,判断添加异步请求是执行run还是将其放入到队列中。run方法真正执行异步请求
class Scheduler{
constructor(max){
this.max = max
this.queue = []
this.running = 0
}
add(task){
return new Promise(resolve=>{
// 关键,加了一层 Promise 的封装,将其 resolve 的函数交出去
task.resolve = resolve
if(this.running<this.max){
this.run(task)
}else{
this.queue.push(task)
}
})
}
async run(task){
if(task && typeof task === 'function'){
this.running++
await task()
task.resolve()
this.running--
const fn = this.queue.shift()
this.run(fn)
}
}
}
题目一
实现了异步并发器,自然我们可以解决下面的笔试题目:
// JS实现一个带并发限制的异步调度器Scheduler,
// 保证同时运行的任务最多有两个。
// 完善代码中Scheduler类,
// 使得以下程序能正确输出
class Scheduler {
constructor() {
this.count = 2
this.queue = []
this.run = []
}
add(task) {
// ...
}
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
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
// 一开始,1、2两个任务进入队列
// 500ms时,2完成,输出2,任务3进队
// 800ms时,3完成,输出3,任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4
题目二
狡猾的面试官还会对上面的题目进行变种,多一个 start 函数控制什么时候开始触发请求,题目如下:
//支持并发的调度器, 最多允许2两任务进行处理
const scheduler = new Scheduler(2)
scheduler.addTask(1, '1'); // 1s后输出’1'
scheduler.addTask(2, '2'); // 2s后输出’2'
scheduler.addTask(1, '3'); // 2s后输出’3'
scheduler.addTask(1, '4'); // 3s后输出’4'
scheduler.start();
分析得知:addTask 只是将异步请求放入到队列中,只有 start 后才会触发请求(或异步操作)。
- 需要一个数组
queue模拟队列,存放异步请求 addTask将异步请求放入队列中start触发开始请求run方法真正执行异步请求
class Scheduler{
constructor(max){
this.max = max
this.queue = []
}
addTask(duration, data){
const p = () => new Promise((resolve)=>{
setTimeout(()=>{
console.log(data, dayjs().format('HH:MM:ss'))
resolve()
}, duration*1000)
})
this.queue.push(p)
}
async run(task){
if(task && typeof task==='function'){
await task()
const fn = this.queue.shift()
this.run(fn)
}
}
start(){
// max 个通道开启
for(let i=0;i<this.max;i++){
const fn = this.queue.shift()
this.run(fn)
}
}
}