JS控制任务并发数

最近经常看到面经中分享的一道题

class Scheduler {
  constructor(concurrency = 2) {
    this.concurrency = concurrency; // 最大并发数
    this.count = 0; // 当前运行时的任务数
    this.waitingPool = []; // 任务等待池
  }

  async add(task) {
      // TODO: 待实现
  }
}

// 初始化一个限流器实例
const scheduler = new Scheduler(2);

// 模拟耗时任务
const timeout = (time) => new Promise(r => setTimeout(r, time));

const addTask = (time, order) => {
  scheduler.add(()=>timeout(time))
    .then(()=>console.log(order))
}

addTask(1000, 1);
addTask(500, 2);
addTask(300, 3);
addTask(400, 4);
addTask(300, 5);

思考

  1. 如何控制并发数?
  2. 当前队列满时如何暂停后面任务执行,空时如何执行后面任务?

实现

class Scheduler {
    constructor(concurrency = 2) {
        // 并发上限
        this.concurrency = concurrency;
        // 运行中的任务数
        this.count = 0;
        // 任务列表
        this.waitingPool = [];
    }
    
    addTask(task) {
        return new Promise((resolve, reject) => {
            // 添加任务到队列里
            this.waitingPool.push({
                task,
                resolve,
                reject,
            });
            // 添加任务之后立即进入执行
            this._run();
        });
    }
    
    _run() {
        // 当任务列表不为空 且 正在运行的任务不超过并发上限 则继续执行下一个任务
        while (this.waitingPool.length > 0 && this.count < this.concurrency) {
            // 队列拿最新的任务
            const { task, resolve, reject } = this.waitingPool.shift();
            
            // 运行计数 +1
            this.count++;
            
            // 运行任务
            const res = task();
            
            // 判断任务是异步还是同步任务
            if (res instanceof Promise) {
                res.then(resolve, reject).finally(() => {
                    // 执行结束 运行计数 -1
                    this.count--;
                    // 递归调用 run 执行下一个任务
                    this._run();
                });
            } else {
                // 执行结束 运行计数 -1
                this.count--;
                // 递归调用 run 执行下一个任务
                this._run();
            }
        }
    }
}