最近导师出了一个题目让我试着解答,完成一个任务调度器,具体题目类似如图所示:
最开始导师只要求一个调度任务执行结束后,才能开始下一个调度任务,时间分别是3s,5s,6s,如图应该是这个要求的进阶版,直接上代码
// 方法一
export default class Schedular {
// number如果不传值则默认为1,符合任务要求
constructor (number = 1) {
// 最大可并发任务数,默认为1
this.number = number
// taskArray用来存储等待执行的任务
this.taskArray = []
// 当前并发任务数
this.count = 0
}
add(scheFunction) {
// 如果当前任务数大于最大任务数,则说明阻塞,放入阻塞队列,否则直接进入start运行
if (this.count >= this.number) {
this.taskArray.push(scheFunction)
} else {
this.start(scheFunction)
}
}
async start(scheFunction) {
// count自增,说明当前任务并发数+1
this.count += 1
// 使用await执行回调函数,等待函数执行完毕才会进入下一步
await scheFunction()
// 执行完毕,当前任务并发数-1
this.count -= 1
// 若阻塞队列中有值,将其shift取出放入start运行
if (this.taskArray.length > 0) {
// 采取递归方法
this.start(this.taskArray.shift())
}
}
}
// 认为此方法最容易理解,第一个调度任务会直接进入start运行,count自增后await执行回调函数,
// 这个等待过程中后面调度任务会同步进入到Schedular中,因为此时count还未自减,所以全部放入阻塞队列中,
// 等await回调函数执行完毕再讲taskArray阻塞队列中的第一个任务拿出执行,最后会发现,其实每一个都完整的执
// 行了start任务,这只针对最大任务数是1的理解,并发数为2,3也可以以此类推,执行方法在最后面,可以直接拿来使用
// 方法二
export default class Schedular2 {
constructor(number = 1) {
// 最大可并发任务数,默认为1
this.number = number
// taskArray用来存储等待执行的任务
this.taskArray = []
// 当前并发任务数
this.count = 0
}
add(scheFunction) {
// 若当前正在执行任务达到最大容量max,则放入taskArray进入阻塞队列,等待前面任务执行完毕后将resolve弹出并执行
if (this.count >= this.number) {
new Promise((resolve) => {
this.taskArray.push(resolve)
}).then(() => {
this.start(scheFunction)
})
} else {
this.start(scheFunction)
}
}
async start(scheFunction) {
this.count += 1
await scheFunction()
// 执行完毕,当前并发任务数--
this.count -= 1
// 若队列中有值,将其resolve弹出并执行
if (this.taskArray.length > 0) {
this.taskArray.shift()()
}
}
}
// 方法二与方法三类似,解析过程在方法三
//方法三
export default class Schedular3 {
constructor(number = 1) {
// 最大可并发任务数,默认为1
this.number = number
// taskArray用来存储等待执行的任务
this.taskArray = []
// 当前并发任务数
this.count = 0
}
async add(scheFunction) {
// 若当前正在执行任务达到最大容量max,则放入taskArray进入阻塞队列,等待前面任务执行完毕后将resolve弹出并执行
if (this.count >= this.number) {
await new Promise((resolve) => {
this.taskArray.push(resolve)
})
}
this.count += 1
await scheFunction()
// 执行完毕,当前并发任务数--
this.count -= 1
// 若队列中有值,将其resolve弹出并执行
if (this.taskArray.length > 0) {
this.taskArray.shift()()
}
}
}
// 此方法为网上对这道题的官方解法,与方法二的差别就是方法三使用await代替了方法二的.then语法,如果当前任务并发数大于
// 最大任务并发数,就放入阻塞队列taskArray,这里的await起到了阻塞队列的真正阻塞作用,此时resolve是未执行的,
// 如果await scheFunction()未完成,阻塞队列会永远阻塞,只有当回调函数执行完成,this.taskArray.shift()()
// 其实就是执行resolve(),这时才会阻塞结束,下一调度任务继续执行,此为最优解答
//调用方法:我未用上图案例执行,用了简化版的执行
// Schedular需要引入
this.schedular = new Schedular()
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
this.schedular.add(() => {
return timeout(3000).then(() => {
console.log("3s后执行完毕")
})
})
this.schedular.add(() => {
return timeout(5000).then(() => {
console.log("5s后执行完毕")
})
})
this.schedular.add(() => {
return timeout(6000).then(() => {
console.log("6s后执行完毕")
})
})
// 执行结果
3s后执行完毕
5s后执行完毕
6s后执行完毕
菜鸟一个,哪里有问题望大佬们及时指出