这段代码创建了一个调度器(Scheduler)类,可以根据任务的优先级调度任务的执行。它使用了堆(通过 peek、pop、push、compare 和 swap 函数)来维护任务队列的优先级和顺序。然后,它使用 scheduleCallback 方法来添加任务,并在适当的时间执行它们。
在示例中,任务按照优先级和延迟被添加到调度器中,并且根据它们的优先级和延迟执行。由于不同任务具有不同的优先级和延迟,因此它们的执行顺序可能会有所不同。
const NoPriority = 0;
const ImmediatePriority = 1;
const UserBlockingPriority = 2;
const NormalPriority = 3;
const LowPriority = 4;
const IdlePriority = 5;
// 获取当前时间
function getCurrentTime() {
return Date.now();
}
class Scheduler {
constructor() {
this.timerQueue = []; // 延时任务队列
this.taskQueue = []; // 任务队列
this.startTime = -1; // 任务开始时间
this.isPerformingWork = false; // 当前是否在执行任务
}
// 开启一个定时器
requestHostTimeout(cbk, time) {
// 设置定时器,在指定的时间后执行回调函数
setTimeout(()=>{
// 绑定this到回调函数的调用中
cbk.bind(this)( getCurrentTime());
}, time);
}
// 定时结束执行任务
handleTimeout(curTime) {
// 调用advanceTimers函数,传入当前时间作为参数
this.advanceTimers(curTime);
// 调用schedulePerformWorkUntilDeadline函数
this.schedulePerformWorkUntilDeadline();
}
// 判断当前任务是否终止
shouldYieldToHost() {
// 获取当前时间与开始时间的差值
const timeElapsed = getCurrentTime() - startTime;
// 如果差值小于帧间隔时间
if (timeElapsed < frameInterval) {
// 返回false
return false;
}
// 返回true
return true;
}
// 将延迟结束的任务添加到任务队列
advanceTimers(curTime) {
// 获取当前时间最早的定时任务
let timerTask = this.peek(this.timerQueue);
while (timerTask) {
if(timerTask.startTime <= curTime) {
// 如果定时任务的开始时间已经过去了
// 延迟时间结束
this.pop(this.timerQueue);
timerTask.sortIndex = timerTask.expirationTime;
// 将该定时任务加入任务队列
this.push(this.taskQueue, timerTask)
} else {
// 如果定时任务的开始时间还未到,则结束循环
return
}
// 获取下一个定时任务
timerTask = this.peek(this.timerQueue);
}
}
// 将任务添加到队列中并进行调度
scheduleCallback(priorityLevel, callback, options) {
// 获取当前时间
// const curTime = Date.now();
const curTime = getCurrentTime();
let timeout;
// 根据优先级设置超时时间
switch (priorityLevel) {
case ImmediatePriority:
timeout = -1;
break;
case UserBlockingPriority:
timeout = 250;
break;
case IdlePriority:
timeout = 1073741823;
break;
case LowPriority:
timeout = 10000;
break;
case NormalPriority:
default:
timeout = 5000;
break;
}
let startTime = curTime;
// 如果提供了选项且选项的delay属性为数字类型,则将startTime更新为当前时间加上选项的delay值
if(options && typeof options.delay === 'number') {
startTime += options.delay;
}
const expirationTime = startTime + timeout;
const task = {
callback,
priorityLevel,
startTime,
expirationTime
}
if(startTime > curTime) {
// 延迟任务加入延迟对列
task.sortIndex = task.startTime;
this.push(this.timerQueue,task);
if(this.peek(this.taskQueue) === null && !this.isPerformingWork) {
// 全为延迟任务,开启一个定时器,带任务延迟结束开始
const timerTask = this.peek(this.timerQueue);
this.requestHostTimeout(this.handleTimeout, timerTask.startTime - curTime)
}
} else {
task.sortIndex = task.expirationTime;
this.push(this.taskQueue, task);
// 执行任务
// 执行任务
if(!this.isPerformingWork) {
this.schedulePerformWorkUntilDeadline();
}
}
}
// 调度执行任务
schedulePerformWorkUntilDeadline() {
// 设置正在执行工作的标志为true
this.isPerformingWork = true;
// 延迟执行,设置延迟时间为0毫秒
setTimeout(() => {
// 获取当前时间作为开始时间
this.startTime = getCurrentTime();
// 执行工作循环
this.workLoop();
// 设置正在执行工作的标志为false
this.isPerformingWork = false;
},0)
}
// 执行任务
workLoop() {
// 调用advanceTimers函数
this.advanceTimers();
// 获取当前任务
let curTask = this.peek(this.taskQueue);
// 循环执行任务
while(curTask) {
const callback = curTask.callback;
// 判断回调函数是否为函数类型
if(typeof callback === 'function') {
const continuationCallback = callback();
// 判断返回的函数是否为函数类型
if(typeof continuationCallback === 'function') {
// 若函数未执行完成,重新赋值给任务
// 函数未执行完成,重新赋值给任务
curTask.callback = continuationCallback;
} else {
// 任务执行完成
// 任务执行完成
this.pop(this.taskQueue);
}
// 调用advanceTimers函数
this.advanceTimers();
} else {
// 任务已完成或 无效,从任务队列中移除
// 任务已完成或 无效 移除
this.pop(this.taskQueue);
}
// 获取下一个任务
curTask = this.peek(this.taskQueue);
}
if (curTask !== null) {
// 若还有任务未完成,则继续执行workLoop函数
return this.workLoop();
} else {
const firstTimer = this.peek(this.timerQueue);
if (firstTimer !== null) {
const curTime = getCurrentTime();
this.requestHostTimeout(this.handleTimeout, firstTimer.startTime - curTime);
}
return false;
}
}
compare (a, b) {
return a.sortIndex < b.sortIndex;
}
swap(heap,index, idx) {
[heap[index],heap[idx]] = [heap[idx], heap[index]]
}
push(heap, value) {
// 将新元素添加到堆中
// 向上调整
heap.push(value);
let index = heap.length - 1;
let parentIdx = (index - 1) >> 1;
while(parentIdx >= 0) {
if(this.compare(heap[index], heap[parentIdx])) {
// 如果当前元素大于其父元素,则交换它们
this.swap(heap,index, parentIdx);
index = parentIdx;
parentIdx = (index - 1) >> 1;
} else {
// 否则,退出循环
break;
}
}
}
pop(heap) {
// 向下调整
// 如果堆的长度为1,直接返回pop出来的值
// 向下调整
if(heap.length === 1) return heap.pop();
// 交换堆顶元素和堆底元素
this.swap(heap, 0, heap.length - 1);
// pop出堆顶元素并赋值给val
const val = heap.pop();
// 初始化父节点索引为0
let parentIdx = 0;
// 初始化左子节点索引和右子节点索引
let leftIdx = parentIdx * 2 + 1;
let rightIdx = parentIdx * 2 + 2;
// 获取堆的当前长度减1
const length = heap.length - 1;
// 当左子节点和右子节点都存在时,执行循环
while(leftIdx < length || rightIdx < length) {
// 如果左子节点不小于父节点且左子节点不小于父节点,说明左子节点满足最小堆性质,结束循环
if(!this.compare(heap[leftIdx], heap[parentIdx]) && !this.compare(heap[rightIdx], heap[parentIdx])) {
break;
// 如果右子节点小于父节点且右子节点大于左子节点,则交换右子节点和父节点,更新父节点索引和右子节点索引,继续执行循环
} else{
if(rightIdx < length &&
this.compare(heap[rightIdx],heap[parentIdx]) &&
this.compare(heap[rightIdx],heap[leftIdx])) {
this.swap(heap, rightIdx, parentIdx);
parentIdx = rightIdx;
leftIdx = parentIdx * 2 + 1;
rightIdx = parentIdx * 2 + 2;
} else if (this.compare(heap[leftIdx],heap[parentIdx])) {
this.swap(heap, leftIdx, parentIdx);
parentIdx = leftIdx;
leftIdx = parentIdx * 2 + 1;
rightIdx = parentIdx * 2 + 2;
}
}
}
// 返回pop出来的值
return val;
}
peek(heap) {
return heap[0] || null;
}
}
const s = new Scheduler();
const task1 = () =>{
console.log('task1 start');
console.log('task1 end');
}
const task2 = () =>{
console.log('task2 start');
console.log('task2 end');
}
const task3 = () =>{
console.log('task3 start');
console.log('task3 end');
}
const task4 = () =>{
console.log('task4 delay start');
console.log('task4 delay end');
}
s.scheduleCallback(4, task1);
s.scheduleCallback(6, task2);
s.scheduleCallback(1, task3);
s.scheduleCallback(1, task4 ,{
delay: 3000
})
// 执行顺序为: task3 task2 task1 task4
将任务添加,根据优先级进行计算,分别将任务加入到延时队列和任务队列,延时任务结束后进入到任务队列,任务队列是一个小顶堆,每次取堆顶执行