🎭 面试官:React 为啥能中断渲染?我:Scheduler 不只是打工,它是调度中心!

81 阅读5分钟

👨‍🏫 本系列由前端面试真题博主 Kincy 发起,每日更新一题,通勤路上轻松掌握高频知识点。
📢 如果你想第一时间获取更新,或与群友交流面试经验、内推信息,欢迎加入微信群(文末扫码)!

📌 系列前言:

面试题千千万,我来帮你挑重点。每天一道,通勤路上、蹲坑时、摸鱼中,技术成长不设限!本系列主打幽默 + 深度 + 面霸必备语录,你只管看,面试场上稳拿 offer!

👮‍♂️ 面试官杀来:

React 的更新机制你知道吧?Fiber 架构你了解过?
那你说说,React 是怎么做到“可中断渲染”的?Scheduler 起了什么作用?

🥊 我反击道:

哼哼,React 的调度系统早就不是 setTimeout 那个水平了。
有请本场 MVP —— 🧠 Scheduler,出场!

🧠 Scheduler 的真本事:它是 React 的“任务调度中心”

Scheduler 干的事很像一个精明的 小助理,每天盯着老板(浏览器)的空闲时间,把 React 的活分优先级安排好,一点点干。

✳️ 1. “时间切片”利器:requestIdleCallback 太慢,React 自己造轮子

Scheduler 不靠 requestIdleCallback(太慢太不稳定),
它直接用 MessageChannelpostMessage 异步调度任务,
创建时间切片(time slice) ,一帧一帧干活,做到:

✅ 不阻塞页面交互
✅ 渲染过程中也能中断 / 暂停 / 恢复!

🎚️ 2. 六种优先级,谁紧谁先上:

Scheduler 内部任务有六种优先级,从高到低如下:

优先级说明常见场景
Immediate立刻干比如同步 setState
UserBlocking用户操作相关输入框、按钮点击
Normal默认优先级异步渲染
Low非关键任务日志收集、预加载
Idle有空再做性能监控、统计任务
NoPriority暂无优先级一般内部使用

Scheduler 会根据优先级,判断是否抢占当前任务
实现类似操作系统里的“协作式多任务调度”。

💣 面试官加码:“那 Scheduler 和 Fiber 有啥关系?”

我端起咖啡,轻轻一笑:

Scheduler 负责 安排谁来上班,Fiber 负责 具体怎么干活

  • Scheduler:任务调度层 → "安排谁来干"
  • Fiber:任务执行层 → "具体怎么干"

一句话:Scheduler 负责说话,Fiber 负责搬砖。

📦 举个例子:模拟一个 React 任务调度流程

import { unstable_scheduleCallback, unstable_NormalPriority } from 'scheduler';

unstable_scheduleCallback(unstable_NormalPriority, () => {
  console.log('我是在未来被调度执行的');
});

这个代码干了啥?
👉 把一个任务按照“正常优先级”安排进了调度队列,等待浏览器空闲时执行。

🛠️ React 内部是怎么用 Scheduler 的?

React 在 render 阶段:

  1. 调用 scheduleUpdateOnFiber() 安排一次更新
  2. 通过 Scheduler 判断任务优先级
  3. 如果有更高优先级任务插队,当前任务就会 被中断
  4. 直到有空,再继续上次的渲染

🧠 核心模块:scheduler 包就是调度引擎!

📁 packages/scheduler

🔧 最重要的两个函数:

// packages/scheduler/src/Scheduler.js

unstable_scheduleCallback(priorityLevel, callback)
unstable_shouldYield()  // 判断是否该中断当前任务

Scheduler 就是靠这两位大哥完成:

  • unstable_scheduleCallback安排任务进调度队列
  • unstable_shouldYield判断时间片是否到了,是不是该让位了

🔬 看一下源码内部做了啥?

function workLoop(hasTimeRemaining, initialTime) {
  let currentTime = initialTime;
  currentTask = peek(taskQueue); // 取出优先级最高的任务

  while (currentTask !== null) {
    if (
      currentTask.expirationTime > currentTime &&
      (!hasTimeRemaining || shouldYieldToHost())
    ) {
      // ⛔️ 当前任务没超时,但时间片到了,先中断
      break;
    }

    const callback = currentTask.callback;
    // 执行任务!
    const continuationCallback = callback();

    if (typeof continuationCallback === 'function') {
      // 如果任务返回的是 continuation,说明没干完,继续等下次
      currentTask.callback = continuationCallback;
    } else {
      // 干完了,出队
      pop(taskQueue);
    }

    currentTask = peek(taskQueue);
  }
}

🎯 核心逻辑:

  • 每次执行前判断 shouldYieldToHost(),如果返回 true,就中断任务;
  • 每个任务可以是可恢复的,如果返回一个函数,就说明“我下次接着干”。

🤔 那这个 shouldYieldToHost 又是啥?

function shouldYieldToHost() {
  return getCurrentTime() >= deadline;
}

非常简单粗暴 —— 看当前时间是不是超了时间片的 deadline。

这个 deadline 是哪来的?

👇 来自 startWorkLoopTimer()

deadline = currentTime + yieldInterval;

默认 yieldInterval = 5ms,也就是说,每个任务最多干 5ms,就要“让位”,防止卡住主线程!

⚙️ 是谁调用了 Scheduler?

别忘了,Scheduler 本身是 通用调度器,不是只为 React 服务。

React 本体中,在 ReactFiberWorkLoop 模块中这样用它👇

import {
  unstable_scheduleCallback,
  unstable_NormalPriority,
} from 'scheduler';

scheduleCallback(NormalPriority, performConcurrentWorkOnRoot);

这个 performConcurrentWorkOnRoot 是干啥的?

它会触发 Fiber 的开始工作:

function performConcurrentWorkOnRoot(root, didTimeout) {
  renderRootConcurrent(root, lanes); // 触发渲染
}

再往下就是熟悉的 beginWork -> completeWork -> commitRoot 流程了。

🔁 React 中断恢复怎么做的?

比如你点个按钮,React 会判断这个更新的优先级(Lane),然后:

  1. 推入任务队列(scheduleUpdateOnFiber)
  2. Scheduler 安排 performConcurrentWorkOnRoot
  3. 执行渲染时,每 5ms 判断要不要中断
  4. 如果被中断,就保留 workInProgress 指针
  5. 下一次恢复从中断位置继续!

React 就像个装修队:施工 5 分钟,听听业主有啥新需求,有就暂停,先干新需求。

🎚️ 优先级系统(Lane Model)和 Scheduler 的关系?

Scheduler 管宏观调度(谁先干),Lane 是 React 自己的优先级系统(干啥先)。

层级作用举例
Scheduler 优先级控制 task 调度节奏点击任务优先于日志任务
React Lane 优先级控制 fiber 树更新顺序setState 优先于 transition

🤹‍♂️ 面霸语录(限时使用)

“React 的并发模式靠的是调度优先级 + 时间切片,Scheduler 是它的 CPU 调度器。”

“Fiber 是双缓冲链表结构,Scheduler 让它按优先级轮流上台演出。”

“你 setState 的时候,Scheduler 已经开始盘算你要不要等一等了。”

🧠 总结一句话:

Scheduler 让 React 渲染变得像打王者荣耀:能插队、能暂停,还能随时换人上场!

🚀 明日预告

面试官:React 的 concurrent rendering 到底怎么运作的?我:Fiber 架构和 Lane 模型,再来整一套!

📌 点赞 + 收藏 + 关注系列!

📚 本系列每天一题,持续更新中!
👉 扫码加入【前端面试题交流群】,一起成长、交流、内推、分享机会!

微信二维码.png 备注“掘金”优先通过