60. 异步调度器
题目描述:
实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
测试用例分析:
时间线: 0ms:任务 1 和 2 开始执行。 1000ms:任务 1 完成,输出 1,任务 3 启动。 2000ms:任务 2 完成,输出 2,任务 4 启动。 3000ms:任务 3 完成,输出 3。 3000ms:任务 4 完成,输出 4。 输出顺序:1 -> 3 -> 2 -> 4。
示例:
scheduler.addTask(1000, "1");
scheduler.addTask(2000, "2");
scheduler.addTask(1000, "3");
scheduler.addTask(1000, "4");
// 输出顺序:1 -> 3 -> 2 -> 4
模版:
class Scheduler {
  // 请输入你的代码
}
// 测试用例
const scheduler = new Scheduler();
// 添加任务(延迟时间,输出值)
scheduler.addTask(1000, "1");
scheduler.addTask(2000, "2");
scheduler.addTask(1000, "3");
scheduler.addTask(1000, "4");
思路:
- 核心设计
- 任务队列 ( - taskQueue):存储待执行的任务,每个任务包含函数、resolve 和 reject。
- 并发控制 ( - maxConcurrent=2):通过- runningCount计数器限制同时运行的任务数。
- 自动调度 ( - \_tryRun):每次添加任务或任务完成时,检查队列并启动新任务。
- 执行流程
- 添加任务:将任务包装为 - Promise并推入队列。
- 尝试执行:若当前运行任务数未达上限,从队列头部取出任务执行。 
- 递归触发:任务完成后,递归调用 - \_tryRun处理后续任务。
参考答案:
DETAILS
class Scheduler {
  constructor() {
    this.maxConcurrent = 2; // 最大并发数
    this.runningCount = 0; // 当前运行中的任务数
    this.taskQueue = []; // 待执行的任务队列
  }
  // 添加任务到调度器
  addTask(delay, value) {
    // 创建任务函数(返回 Promise)
    const task = () =>
      new Promise((resolve) => {
        setTimeout(() => {
          console.log(value); // 延迟结束后输出值
          resolve();
        }, delay);
      });
    // 将任务封装为 Promise 并加入队列
    return new Promise((resolve, reject) => {
      this.taskQueue.push({ task, resolve, reject });
      this._tryRun(); // 尝试启动任务
    });
  }
  // 尝试执行任务(内部方法)
  _tryRun() {
    while (
      this.runningCount < this.maxConcurrent &&
      this.taskQueue.length > 0
    ) {
      const { task, resolve, reject } = this.taskQueue.shift();
      this.runningCount++; // 增加运行计数
      task()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.runningCount--; // 任务完成,减少计数
          this._tryRun(); // 递归触发下一个任务
        });
    }
  }
}
// 测试用例
const scheduler = new Scheduler();
// 添加任务(延迟时间,输出值)
scheduler.addTask(1000, "1");
scheduler.addTask(2000, "2");
scheduler.addTask(1000, "3");
scheduler.addTask(1000, "4");
// 输出顺序:1 -> 3 -> 2 -> 4
