功能说明
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() 可在结束后释放状态。