一、MessageChannel 实现时间切片的核心原理(3句话)
- 浏览器渲染是每帧 16ms,JS 不能霸占主线程太久,否则卡顿。
- MessageChannel 能快速产生一个宏任务 ,比
setTimeout更快、更稳。 - 每执行 5ms 就主动暂停,通过宏任务让出主线程让浏览器渲染,然后再继续。
这就是 时间切片(Time Slicing)。
二、我给你封装:终极通用切片方法(直接复制用)
这个方法 接收一个大任务,自动分片,每片5ms,不阻塞页面。
javascript
/**
* 基于 MessageChannel 的时间切片工具
* @param {Function} taskFn 每次执行一小步的函数,return true 表示全部完成
* @param {Function} onFinish 完成回调
*/
function useTimeSlice(taskFn, onFinish) {
let isStop = false;
const channel = new MessageChannel();
const port1 = channel.port1;
const port2 = channel.port2;
function runSlice() {
if (isStop) return;
const start = performance.now();
while (performance.now() - start < 5) {
const finished = taskFn();
if (finished) {
onFinish?.();
return;
}
}
port1.postMessage(null);
}
port2.onmessage = runSlice;
runSlice();
// 返回清理方法
return () => {
isStop = true;
port2.onmessage = null;
port1.close();
port2.close();
};
}```
---
三、使用示例(超级简单)
你有一个巨量循环任务,直接用:
javascript
let i = 0;
const total = 100000; // 超大任务
// 直接使用!
const stopTask = useTimeSlice(
// 每一步小任务
() => {
console.log("执行:", i);
i++;
return i >= total; // 做完返回 true
},
// 完成回调
() => {
console.log("✅ 全部完成!");
}
);
// 想停就停
// stopTask()
在 React 里使用:
js
useEffect(() => {
const stop = useTimeSlice(..., ...);
return stop; // 卸载自动清理
}, []);
四、这个方法到底做了什么?
- 你的大循环不会一次执行完
- 每 5ms 自动切一刀
- 时间到自动暂停,让浏览器去渲染页面
- 渲染完立刻继续
- 页面永远不卡顿
这就是 React 内部做时间切片的方式。
五、面试标准答案(背会)
MessageChannel 实现时间切片的原理:
- 使用
MessageChannel创建快速宏任务 ,比setTimeout更快更稳定。 - 通过
performance.now()计算执行时间。 - 每执行 5ms 就主动暂停,让出主线程。
- 通过
postMessage触发下一轮任务,实现可中断、可恢复。 - 最终达到大任务分片执行,不阻塞渲染的效果。
六、你只要记住一句话
MessageChannel 负责快速调度,5ms 负责控制时间片,
两者结合 = 完美时间切片,就是 React Scheduler 核心。