故事发生在我看到一篇头条笔试题文章...

922 阅读2分钟

背景

今天在《前端技术江湖》公众号看到一片文章:头条前端笔试题 - 实现一个带并发限制的promise异步调度器,虽然运行结果相同,但是作者在实现的过程中对方法的定义都进行修改了,因此笔者按照自己的方法实现了这个异步调度器。

题目描述

JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个。完善下面代码中的 Scheduler 类,使得以下程序能正确输出。

class Scheduler {
  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(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

题目分析

  • 最后执行的是 addTask 方法,那我们首先从这个方法入手。函数执行体中执行了 scheduler.add(fn),这个方法后面紧接着 then 方法,意味着 scheduler.add 返回的是一个 Promise 对象。
  • 按照题意,当前最多只能有两个任务在运行,那么我们在 Scheduler 类中定义一个任务队列 tasks 属性,定义一个当前正在运行的任务数量 runningTaskCount 属性,当 runningTaskCount 小于2 时,马上执行 add(promiseCreator) 中的 promiseCreator 函数,否则当某一个任务执行完后再执行一个新的任务。

直接上代码了

class Scheduler {
  constructor() {
    this.tasks = []
    this.runningTaskCount = 0
    this.resolveArr = []
  }
  add(promiseCreator) {
    this.tasks.push(promiseCreator)
    return new Promise((resolve) => {
      this.resolveArr.push(resolve)
      this.run()
    })
  }
  run() {
    if (this.runningTaskCount < 2 && this.tasks.length > 0) {
      this.runningTaskCount++
      const resolveFn = this.resolveArr.shift()
      const task = this.tasks.shift()
      task().then(() => {
        this.runningTaskCount--
        resolveFn()
        this.run()
      })
    }
  }
}

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')