时间切片
// src/scheduler.js
// 流程:scheduleCallback => planWork => flushWork => flush => planWork=>...
import { push, pop, peek } from "./heapify";
// 任务队列
let taskQueue = [];
// 当前回调函数
let currentCallback = null;
let frameDeadline = 0;
// 切片时间长度
const frameLength = 1000 / 60;
export function scheduleCallback(callback) {
// 获取更精确的微妙级别的时间
const currentTime = performance.now();
const startTime = currentTime;
const timeout = 3000;
// 过期时间
const dueTime = startTime + timeout;
let newTask = {
callback,
startTime,
dueTime
};
// 装入任务队列
push(taskQueue, newTask);
currentCallback = flush;
planWork();
}
// 观察任务执行情况
function flush(iniTime) {
let currentTime = iniTime;
let currentTask = peek(taskQueue);
while (currentTask) {
// 如果任务过期并且切片时间到了跳出循环
if (
currentTask.dueTime > currentTime &&
performance.now() >= frameDeadline
) {
break;
}
let callback = currentTask.callback;
currentTask.callback = null;
// 完成了没有
const didout = currentTask.dueTime <= currentTime;
let next = callback(didout);
// 没完成继续完成了移除任务
next ? (currentTask.callback = next) : pop(taskQueue);
// 接着执行任务或者开启下一个任务
currentTask = peek(taskQueue);
currentTime = performance.now();
}
return !!currentTask;
}
function flushWork() {
if (currentCallback) {
let currentTime = performance.now();
frameDeadline = currentTime + frameLength;
let more = flush(currentTime);
more ? planWork() : (currentCallback = null);
}
}
// 使用消息轨道还是定时器处理任务
export const planWork = (() => {
if (typeof MessageChannel !== "undefined") {
const { port1, port2 } = new MessageChannel();
port1.onmessage = flushWork;
return () => port2.postMessage(null);
}
return () => setTimeout(flushWork);
})();