在去年遇到一个面试题:
设计一个异步任务的调度器,最多只能同时有两个异步任务在执行。执行完的异步任务出队列,待执行的异步任务按照添加顺序依次入队列。
class Scheduler {
constructor() {...}
add(promiseCreator) {...}
}
//用例:
const timeout = time =>
new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(time, order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// 输出:
// 500 '2'
// 300 '3'
// 1000 '1'
// 400 '4'
当时没写出来,回去发(复)奋(制)图(粘)强(贴)写了一个async await版本的:
async await版本:
class Scheduler {
constructor() {
this.waitQueue = []
this.count = 0
}
async add(promiseCreator) {
this.count >= 2 && await new Promise(resolve => this.waitQueue.push(resolve))
this.count++
const res = await promiseCreator()
this.count--
this.waitQueue.length && this.waitQueue.shift()()
return res
}
}
const timeout = time =>
new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(time, order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// 执行结果:
// 500 '2'
// 300 '3'
// 1000 '1'
// 400 '4'
执行流程:
async函数是自带执行器的generator,即前者是后者的语法糖,核心是一样的。它们可以让一个函数分段执行,当函数执行遇到await和yield的时候, 函数会交出执行权。所以上面的程序执行流程如下:
- 同步任务:
addTask(1000, '1')执行到const res = await promiseCreator()的时候,交出执行权,执行下一步addTask(500, '2')同1addTask(300, '3')此时count = 2所以执行到await new Promise(resolve => this.waitQueue.push(resolve))的时候交出执行权,执行下一步addTask(400, '4')同3
- 异步任务:
time=500的定时器resolve,addTask(500, '2')函数重获执行权,开始执行其中的后续代码:
this.count-- //count = 1
this.waitQueue.length && this.waitQueue.shift()() //使waitQueue中的第一个异步任务resolve,addTask(300, '3')重获执行权
return res //遇到return,则async函数返回的Promise的状态resolved, 所以.then(() => console.log(time, order))输出 500 '2'
addTask(300, '3')函数重获执行权,执行到const res = await promiseCreator()交出执行权time=300的定时器resolve, 后续流程同5addTask(400, '4')函数重获执行权, 后续同6time=1000的定时器resolve, 后续流程同5time=400的定时器resolve, 后续流程同5
promise版本:
后来复习到Promise的时候,就试着用Promise写了一个版本
class Scheduler {
constructor() {
this.promiseCreatorQueue = []
this.waitQueue = []
this.count = 0
}
add(promiseCreator) {
if (this.count >= 2) return new Promise(resolve => {
this.waitQueue.push(resolve)
this.promiseCreatorQueue.push(promiseCreator)
}).then(() => {
this.count++
return this.promiseCreatorQueue.shift()().then(() => {
this.count--
this.waitQueue.length && this.waitQueue.shift()()
})
})
this.count++
return promiseCreator().then(() => {
this.count--
this.waitQueue.length && this.waitQueue.shift()()
})
}
}
const timeout = time =>
new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(time, order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// 执行结果:
// 500 '2'
// 300 '3'
// 1000 '1'
// 400 '4'
执行流程
详细的流程就不写了,大概的流程就是:每个promiseCreator()返回的Promise resolved之后开始开始执行下一个promiseCreator来开启一个新的异步任务
总结
由代码看以看出,async await很大地简化了代码和逻辑。