从零实现一个迷你 React 调度器
用几十行代码实现一个带优先级与时间切片的任务调度器,理解 React 并发渲染的核心机制。
理解复杂系统的捷径,往往是亲手实现一个简化版。本文带你写一个迷你调度器,理解 React 是如何在「不卡顿」与「完成任务」之间取得平衡的。
核心矛盾
浏览器主线程同一时间只能做一件事。如果 React 一次渲染耗时 200ms,就会掉帧。调度器要解决的核心问题是:如何在长时间任务中穿插高优先级工作,并适时让出主线程。
时间切片
一个朴素的想法:把任务拆成小片,每执行一小片,就检查是否需要让出主线程。
function workLoop() {
while (tasks.length > 0 && !shouldYield()) {
const task = tasks.shift();
task.run();
}
if (tasks.length > 0) {
requestIdleCallback(workLoop);
}
}
function shouldYield() {
return performance.now() - startTime > 5; // 5ms 让出一次
}
shouldYield 检查当前时间片是否耗尽,是调度器的基础。
优先级队列
真实场景里任务有轻重缓急。我们用小顶堆维护优先级:
class PriorityQueue {
// 按 priority 排序,数字越小优先级越高
push(task) { /* 插入并上浮 */ }
pop() { /* 取出最小并下沉 */ }
}
高优先级任务(如用户输入)会插队到低优先级任务(如大数据渲染)之前。
可中断的循环
把「任务执行」与「让出检查」结合,就得到一个可中断的 workLoop:
function workLoop(deadline) {
let shouldYield = false;
while (currentTask && !shouldYield) {
// 执行一个工作单元
shouldYield = deadline.timeRemaining() < 5;
if (shouldYield) break;
}
if (currentTask) {
requestIdleCallback(workLoop);
}
}
React 的 Fiber 架构正是这种「可中断单元 + 调度」思想的完整实现。
结语
迷你实现的价值在于抓住本质:调度 = 任务拆分 + 优先级 + 时间切片。理解了这三要素,再看 React 的 Scheduler 源码,就会觉得清晰许多。