从零实现一个迷你 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 源码,就会觉得清晰许多。