用async 与 promise实现异步任务调度

1,602 阅读3分钟

在去年遇到一个面试题:

设计一个异步任务的调度器,最多只能同时有两个异步任务在执行。执行完的异步任务出队列,待执行的异步任务按照添加顺序依次入队列。

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,即前者是后者的语法糖,核心是一样的。它们可以让一个函数分段执行,当函数执行遇到awaityield的时候, 函数会交出执行权。所以上面的程序执行流程如下:

  • 同步任务:
  1. addTask(1000, '1')执行到const res = await promiseCreator()的时候,交出执行权,执行下一步
  2. addTask(500, '2')同1
  3. addTask(300, '3')此时count = 2 所以执行到await new Promise(resolve => this.waitQueue.push(resolve))的时候交出执行权,执行下一步
  4. addTask(400, '4')同3
  • 异步任务:
  1. 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'
  1. addTask(300, '3')函数重获执行权,执行到const res = await promiseCreator()交出执行权
  2. time=300的定时器resolve, 后续流程同5
  3. addTask(400, '4')函数重获执行权, 后续同6
  4. time=1000的定时器resolve, 后续流程同5
  5. time=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很大地简化了代码和逻辑。