实现一个简单的 Scheduler(调度器、JavaScript)

50 阅读2分钟

控制任务执行顺序、并发数限制、异步任务管理

实现一个支持「并发控制 + 任务队列 + 异步兼容」的轻量调度器类

1. 代码实现

class Scheduler {
  /**
   * 构造函数
   * @param {number} limit - 最大并发数(默认1)
   */
  constructor(limit = 1) {
    this.limit = limit // 并发限制
    this.running = 0 // 正在执行的任务数
    this.queue = [] // 任务队列(待执行)
  }

  /**
   * 添加任务到调度器
   * @param {Function} task - 任务函数(支持同步/异步,异步需返回Promise)
   * @returns {Promise} - 任务执行结果的Promise
   */
  add(task) {
    return new Promise((resolve, reject) => {
      // 将任务和其resolve/reject存入队列
      this.queue.push({ task, resolve, reject })
      // 尝试执行下一个任务
      this.runNext()
    })
  }

  /**
   * 执行下一个队列中的任务
   */
  runNext() {
    // 若当前并发数未达限制,且队列有任务
    if (this.running < this.limit && this.queue.length > 0) {
      this.running++ // 并发数+1
      const { task, resolve, reject } = this.queue.shift() // 取出队列首个任务

      try {
        // 执行任务(处理同步/异步)
        const result = task()
        // 若任务返回Promise(异步)
        if (result instanceof Promise) {
          result
            .then((res) => resolve(res))
            .catch((err) => reject(err))
            .finally(() => {
              this.running-- // 并发数-1
              this.runNext() // 递归执行下一个任务
            })
        } else {
          // 同步任务直接resolve
          resolve(result)
          this.running--
          this.runNext()
        }
      } catch (err) {
        // 捕获任务执行异常
        reject(err)
        this.running--
        this.runNext()
      }
    }
  }
}

2. 使用示例

// 1. 创建调度器,限制最大并发数为2
const scheduler = new Scheduler(2);
const startTime = Date.now()

// 2. 定义异步任务(模拟接口请求)
const asyncTask = (id, delay) => {
  return () =>
    new Promise((resolve) => {
      setTimeout(() => {
        console.log(`时间:${Date.now() - startTime},任务${id}执行完成`);
        resolve(id);
      }, delay);
    });
};

// 3. 添加任务到调度器
scheduler.add(asyncTask(1, 1000)); // 任务1:延迟1000ms
scheduler.add(asyncTask(2, 500));  // 任务2:延迟500ms
scheduler.add(asyncTask(3, 800));  // 任务3:延迟800ms
scheduler.add(asyncTask(4, 300));  // 任务4:延迟300ms

// 执行结果(并发数=2,按队列顺序+完成顺序调度):
// 任务2(500ms)→ 任务1(1000ms)→ 任务4(300ms)→ 任务3(800ms)
// 打印顺序:
// 时间:503,任务2执行完成
// 时间:1001,任务1执行完成
// 时间:1303,任务4执行完成
// 时间:1317,任务3执行完成