场景
2023年03月19日
一、React 分片渲染
const works = [
() => {
console.log("任务一 开始");
sleep(20);
console.log("任务一 结束");
},
() => {
console.log("任务二 开始");
sleep(10);
console.log("任务二 结束");
},
() => {
console.log("任务三 开始");
sleep(0);
console.log("任务三 结束");
},
];
function sleep(delay) {
for (let ts = Date.now(); Date.now() - ts <= delay; ) {}
}
function workLoop(deadline) {
console.log(`当前帧剩余时间为: ${deadline.timeRemaining()}`);
while (deadline.timeRemaining() > 1 && works.length > 0) {
performUnitOfWork();
}
if (works.length > 0) {
requestIdleCallback(workLoop);
}
}
function performUnitOfWork() {
let work = works.shift();
work();
}
requestIdleCallback(workLoop);
二、批量任务空闲调度
function requestIdleCallbackOfSetTimeout() {
return (cb, { timeout } = {}) => {
const start = performance.now();
const callbackWrapper = () => {
const now = performance.now();
const didTimeout = timeout && now - start >= timeout;
if (didTimeout || now - start < 50) {
cb({
didTimeout,
timeRemaining() { return Math.max(0, 50 - (now - start)); },
});
} else {
window.setTimeout(callbackWrapper);
}
};
window.setTimeout(callbackWrapper);
};
}
function requestIdleCallbackOfMessageChannel() {
return (cb, { timeout } = {}) => {
const start = performance.now();
const channel = new MessageChannel();
channel.port2.onmessage = () => {
const now = performance.now();
const didTimeout = timeout && now - start >= timeout;
if (didTimeout || now - start < 50) {
cb({
didTimeout,
timeRemaining() { return Math.max(0, 50 - (now - start)); },
});
} else {
channel.port1.postMessage(undefined);
}
};
channel.port1.postMessage(undefined);
};
}
function polyfillRequestIdleCallback() {
if (typeof window.requestIdleCallback === 'function') {
return window.requestIdleCallback.bind(window);
} if (typeof MessageChannel === 'function') {
return requestIdleCallbackOfMessageChannel();
}
return requestIdleCallbackOfSetTimeout();
}
class Scheduler {
constructor() {
this.tasks = [];
this.callbackID = null;
this.requestIdleCallback = polyfillRequestIdleCallback();
}
add(task) {
this.tasks.push(task);
if (!this.callbackID) {
this.start();
}
}
start() {
this.callbackID = this.requestIdleCallback((deadline) => {
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && this.tasks.length > 0) {
const task = this.tasks.shift();
task();
}
if (this.tasks.length > 0) {
this.start();
} else {
window.cancelIdleCallback(this.callbackID);
this.callbackID = null;
}
});
}
}
module.exports = Scheduler;