← 中文资源

简体中文版本

支持优先级的延迟任务调度器

调度延迟异步任务,支持优先级、并发上限、取消、错误隔离,以及等待全部完成的 drain。

阅读英文原文

功能说明

TaskScheduler 接受带延迟和优先级的同步或异步函数。延迟结束后任务进入就绪队列,调度器按优先级从高到低、同优先级按提交顺序取出任务,并保证同时运行数不超过 concurrency。它适合延迟发送通知、定时清理、批量任务错峰执行等场景。

每个任务返回一个句柄,包含 id 和 cancel();取消只对尚未开始的任务有效。任务抛出的错误会被隔离:调度器调用 onError 后继续处理后续任务,不会让整个队列停止。drain() 返回一个 Promise,在队列为空且没有运行中任务时 resolve,便于测试和优雅退出。

关键代码

就绪队列在每次调度前排序,然后按并发额度启动任务:

this.ready.sort(
  (left, right) =>
    right.priority - left.priority || left.sequence - right.sequence,
);
while (this.running < this.concurrency && this.ready.length > 0) {
  const job = this.ready.shift();
  if (job === undefined || job.settled) continue;
  this.running += 1;
  Promise.resolve()
    .then(() => job.run())
    .catch((error) => this.onError(error, job.id))
    .finally(() => {
      this.running -= 1;
      this.jobs.delete(job.id);
      this.flush();
    });
}

使用注意

优先级只在任务已经进入就绪队列后生效。如果两个任务同时到期,而调度器在第一个任务到期时立即启动它,第二个任务可能来不及参与排序;示例通过先占用唯一并发槽位,让后续任务在队列中等待,从而观察优先级。延迟使用 setTimeout,长延迟受系统定时器精度和进程存活时间影响,不适合持久化调度。

cancel() 对运行中的任务返回 false,需要协作式取消时应额外传入 AbortSignal。close() 会清空待执行任务并让 drain 提前完成,进行中的任务仍会继续运行。onError 自身抛出的异常会被吞掉,避免破坏调度循环。调度器不持久化任务,进程重启后未执行的任务会丢失。

验证方式

示例设置并发 1,先启动一个运行 80 毫秒的任务,再分别提交低优先级、高优先级、被取消的高优先级和会抛错的任务。最终事件顺序应为 start, end, high, error 5: boom, low,被取消的任务不出现。await scheduler.drain() 返回后打印事件数组,证明所有就绪任务已处理完毕;close() 可在结束后释放状态。