Skip to content
本页目录

JS 如何实现异步并发控制器

异步控制器的概念以及作用

概念

异步编程是前端常用的一种编成方式,也是一种解决队列阻塞的优化方案,该方案可以使程序在未完成情况下,不阻塞进程继续等待执行结果,该过程其他代码正常执行。

作用

为了保证异步操作的顺序和优先级,或者避免过多的异步程序占用资源导致内存溢出,设计出一种独立于EventLoop之外的异步队列,该队列可以根据配置设置并发数量和先后顺序。 异步并发器保持先进先出的顺序,等待前者执行完毕,按照顺序仅需填充执行队列,已执行完毕则返回promise对象。 这样做的好处就是限制同时进行的异步任务数量,避免不必要的资源浪费,另外可以控制执行顺序。

具体实现

ts
class Scheduler {
  max: number
  queue: Promise[]
  task: number
  constructor(max) {
    // 任务最大并发数量
    this.max = max
    // 任务队列
    this.queue = []
    // 当前正在执行的任务数量,控制不能超过并发量
    this.task = 0
  }

  // 添加任务
  async addTask(promiseExec) {
    /**
     * 如果队列当前任务数量(task)大于最大并发量max,则需阻塞任务添加, 要将新任务通过resolve进行封装添加到队列等待唤醒
     * 等待task小于 max时,将任务从resolve中取出,await 放入队列进行执行,本次add操作继续
    */

    if (this.task >= this.max)
      await new Promise(resolve => this.queue.push(resolve))

    this.task++
    let res = null

    try {
      res = await promiseExec()
      this.task--
      // 如果有待唤醒的add操作,则取一个执行
      if (this.queue.length) {
        // 保持先进先出,从头部取一个add resolve任务进行执行
        // 以便等待的添加add任务继续执行  添加操作
        const addExec = this.queue.shift()
        addExec()
      }
    }
    catch (error) {
      console.log(error)
    }
    return res
  }
}

功能测试

ts
// 构建一个新的并发器
const scheduler = new Scheduler(2)

// 设置一个异步等待器
const sleep = (time: number) => new Promise(resolve => setTimeout(() => resolve(), time))

// 设置添加异步任务方法
function addTask(time, order) {
  // scheduler.addTask 返回一个promise,也需要导入一个promise
  scheduler.addTask(() => sleep(time).then(() => console.log(order)))
}

addTask(1000, '1')
addTask(300, '2')
addTask(500, '3')
addTask(800, '4')

// 顺序如下: 2 -> 3 -> 1 -> 4