调度系统和调和系统的桥梁

一、ensureRootIsScheduled

登记待调度根

ini 复制代码
export function ensureRootIsScheduled(root) {
  // 当前 root 就是链表尾 lastScheduledRoot 或 该 root 已经在链表中
  if (root === lastScheduledRoot || root.next !== null) {

  } else {
    if (lastScheduledRoot === null) {
      // 待调度根链表头节点
      firstScheduledRoot = lastScheduledRoot = root;
    } else {
      // 待调度根链表尾节点
      lastScheduledRoot.next = root;
      lastScheduledRoot = root;
    }
  }
  mightHavePendingSyncWork = true;

  ensureScheduleIsScheduled();

}

export function ensureScheduleIsScheduled() {
  if (!didScheduleMicrotask) { 
    // 是否已经注册了根批量调度微任务
    didScheduleMicrotask = true;
    scheduleImmediateRootScheduleTask();
  }
}

作用

  • 全局调度根链表维护 :维护一条全局单向链表 收集所有存在待更新的 FiberRoot,支持多根应用(多 ReactDOM.createRoot 实例)批量统一处理
    • 当前 root 就是链表尾 lastScheduledRoot 或该 root 已经在链表中,表示这个 root 已经登记过待调度,无需重复入队,避免重复链表操作
    • 否则把 root 加入待调度链表
  • 全局标记当前存在待同步 / 并发渲染任务 ,在 flushSync、批量同步刷新逻辑中会读取该标记
    • 若为 false,可直接快速退出同步刷新逻辑,不用遍历调度链表
    • 只要任意根新增更新,立刻置为 true,提示同步刷新逻辑需要遍历链表处理任务
  • 保证全局存在且仅存在一个处理根调度的微任务,用来批量处理链表内所有待调度根。当前没有排队的调度微任务时,才走创建逻辑
    • 同步设置锁,避免并发多次进入创建分支,保证微任务全局唯一
    • 创建唯一批量处理微任务,把所有根的调度计算延后到当前同步代码执行完毕后统一处理

设计意义

  • 支持多 root 应用 (如 portals 或微前端)
  • 合并同步多轮更新 :同步代码中连续多次 setState(循环、事件内多次更新),只会触发一次微任务,只遍历一次根链表,只创建一次 Scheduler 任务
  • 幂等防护 :多次调用 ensureScheduleIsScheduled 也只会触发一次微任务创建,是批量更新合并的关键锁

二、scheduleImmediateRootScheduleTask

负责创建批量处理根调度的异步任务,自动兼容浏览器微任务能力、规避渲染 / 提交上下文死循环,是连接同步更新登记与批量渲染调度的中间层

javascript 复制代码
function scheduleImmediateRootScheduleTask() {
  if (supportsMicrotasks) {
    scheduleMicrotask(() => {
      // 检查是否在 render/commit 中(在 Safari 中,iframe 的加载会强制刷新微任务队列)
      if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
        // 若在渲染中,用宏任务替代(防止无限循环)
        Scheduler_scheduleCallback(ImmediateSchedulerPriority, processRootScheduleInImmediateTask);
        return;
      }
      processRootScheduleInMicrotask();
    });
  } else {
    Scheduler_scheduleCallback(ImmediateSchedulerPriority, processRootScheduleInImmediateTask);
  }
}

function processRootScheduleInImmediateTask() {
  processRootScheduleInMicrotask();
}

作用

  • 浏览器支持 queueMicrotask:优先走微任务批量处理
    • 当前主线程是否正处在 render 调和阶段commit 提交阶段 :放弃微任务执行,改用 Scheduler 即时优先级宏任务延后批量调度至当前渲染栈完全结束
    • 否则直接执行微任务批量逻辑,同一同步周期所有更新合并一次处理,减少 Scheduler 碎片任务
  • 不支持微任务 / 当前处于渲染 / 提交上下文:降级为 Scheduler 即时优先级宏任务

设计意义

  • 优先微任务保证批量更新粒度 :微任务在当前同步调用栈清空后、浏览器渲染前执行,同一事件循环内所有同步 setState 会被完全合并,不会被页面渲染切割
  • 上下文安全防护 :区分 "空闲主线程" 和 "正在渲染 / 提交" 两种场景,动态切换微 / 宏任务,从底层规避并发渲染死循环,绝不允许批量调度逻辑打断正在进行的 render /commit 流程

三、processRootScheduleInMicrotask

批量消费全局待调度根链表

javascript 复制代码
function processRootScheduleInMicrotask() {
  didScheduleMicrotask = false;       // 重置去重标志
  // 默认清空 "存在待同步渲染任务" 标记,遍历所有根时再重新赋值
  mightHavePendingSyncWork = false;

  // 检查当前事件的 transition lane
  let syncTransitionLanes = NoLanes;
  // 本次用户交互事件(点击、跳转、popstate)产生的 useTransition 专属车道
  if (currentEventTransitionLane !== NoLane) {
    if (shouldAttemptEagerTransition()) {
      // popstate等场景强制同步渲染transition,保留滚动位置
      syncTransitionLanes = currentEventTransitionLane; 
    } else if (enableDefaultTransitionIndicator) {
      // 提前刷新Default车道,区分加载指示器逻辑
      syncTransitionLanes = DefaultLane;             
    }
  }

  let prev = null;
  let root = firstScheduledRoot;

  // 遍历所有 root,为每个 root 安排任务
  while (root !== null) {
    const next = root.next;
    // 为当前根创建Scheduler渲染任务,返回剩余待处理车道
    const nextLanes = scheduleTaskForRootDuringMicrotask(root, currentTime);
    // 分支1:当前根无剩余待更新车道,从链表移除
    if (nextLanes === NoLane) {
      // 该 root 无待处理工作 → 从调度链表中移除
      root.next = null;
      // 链表操作
      if (prev === null) {
        
        firstScheduledRoot = next;
      } else {
        prev.next = next;
      }
      if (next === null) {
        
        lastScheduledRoot = prev;
      }
      // 分支2:根仍有未处理更新,保留在链表中
    } else {
      prev = root;
      // 判断是否包含同步/手势过渡车道,更新mightHavePendingSyncWork
      if (syncTransitionLanes !== NoLanes || 
      includesSyncLane(nextLanes) ||
        (enableGestureTransition && isGestureRender(nextLanes))) {
        mightHavePendingSyncWork = true;
      }
    }
    root = next;
  }

  // 最后刷新同步工作
  // 检查是否有 ViewTransition 正在运行
  // 如果有,跳过同步刷新,等 ViewTransition 完成后再执行
  if (!hasPendingCommitEffects()) {
    flushSyncWorkAcrossRoots_impl(syncTransitionLanes, false);
  }
  if (currentEventTransitionLane !== NoLane) {
    currentEventTransitionLane = NoLane;
    startDefaultTransitionIndicatorIfNeeded();
  }
}

作用

  • 释放全局微任务锁:解锁微任务锁,允许下一轮更新重新创建调度微任务
  • 初始化全局同步任务标记:清空 "存在待同步渲染任务" 标记,遍历所有根时再重新赋值
  • 处理事件触发的 Transition 同步渲染车道
    • 页面前进后退需要同步渲染 Transition,保证滚动位置不丢失,把过渡车道标记为同步执行
    • 开启默认过渡指示器:提前刷新 Default 优先级任务,区分普通 setState 和过渡加载状态,避免指示器逻辑错乱
  • 遍历 firstScheduledRoot 单向链表,逐个为根创建并发渲染任务
    • 根无剩余工作,移除链表
      • 清空 root.next,标记该根脱离调度链表,清理无待更新的根、维护链表首尾指针
      • 分头部 / 中间 / 尾部节点更新 firstScheduledRootlastScheduledRoot 全局指针
    • 根仍有待更新,保留链表
      • 更新 prev 指针,维持链表遍历
      • 判断当前根剩余车道是否包含:同步 Transition 车道、同步车道、手势过渡渲染车道
      • 满足任一条件则置 mightHavePendingSyncWork = true,标记全局存在同步渲染任务
  • 判断是否存在未执行的被动副作用(useEffect)、提交挂起任务
    • 若存在待提交副作用,跳过本次同步刷新,等待副作用执行完毕再处理同步渲染,防止时序冲突
    • 批量执行同步渲染任务(同步 TransitionSyncLane 手势过渡),批量遍历所有根,同步执行 syncTransitionLanes 标记的过渡任务、全局 SyncLane 同步更新,直接同步渲染 DOM,不进入 Scheduler 异步分片
  • 重置事件过渡全局变量、启动默认过渡加载指示器
    • 清空本次交互过渡车道,下一次交互分配全新过渡车道
    • 根据本次过渡渲染结果,判断是否展示页面加载过渡指示器

设计意义

  • 锁生命周期闭环:微任务执行开头释放锁,下一次更新可正常走调度流程
  • 单一微任务批量消费,极致合并更新 :同一事件循环内所有同步 setState、多根更新全部收拢到一次微任务遍历,一次性批量创建所有渲染任务,杜绝碎片化 Scheduler 任务,是 React 批量更新的底层核心
  • 同步渲染后置执行:放在链表遍历完成后,避免同步渲染打断链表遍历逻辑
  • 副作用时序隔离 :存在 pending 副作用时暂缓同步渲染,保证 commiteffect、渲染顺序规范

四、scheduleTaskForRootDuringMicrotask

不做同步渲染,只负责向 Scheduler 注册 / 复用 / 取消渲染任务

javascript 复制代码
function scheduleTaskForRootDuringMicrotask(root, currentTime) {
  // 1. 检查是否有 lane 饿死 → 标记过期
  markStarvedLanesAsExpired(root, currentTime);

  // 2. 获取下一个要处理的 lanes
  
  // 存在待执行 useEffect 的根
  const rootWithPendingPassiveEffects = getRootWithPendingPassiveEffects();
  // 触发被动副作用对应的车道
  const pendingPassiveEffectsLanes = getPendingPassiveEffectsLanes();
  // 当前正在调和的根及其渲染车道
  const workInProgressRoot = getWorkInProgressRoot();
  // 当前正在调和的根的渲染车道
  const workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes();
  // 根存在挂起提交(视图过渡、Suspense 提交中断)
  const rootHasPendingCommit =
    root.cancelPendingCommit !== null || root.timeoutHandle !== noTimeout;
  const nextLanes =
    enableYieldingBeforePassive && root === rootWithPendingPassiveEffects
      ? pendingPassiveEffectsLanes
      : getNextLanes(
          root,
          root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
          rootHasPendingCommit,
        );
  // 获取当前根已存在的调度任务,用于后续对比优先级、取消旧任务      
  const existingCallbackNode = root.callbackNode;
  if (nextLanes === NoLanes || 
      (root === workInProgressRoot && isWorkLoopSuspendedOnData()) ||
      root.cancelPendingCommit !== null
  ) {
    // 无事可做 → 取消已存在的 callback
    if (existingCallbackNode !== null) {
      cancelCallback(existingCallbackNode);
    }
    root.callbackNode = null;
    root.callbackPriority = NoLane;
    return NoLane;
  }

  // 3. 确定优先级并调度
  if (includesSyncLane(nextLanes)) {
    // 同步 lanes → 不通过 Scheduler(微任务结束时会同步刷新)
    if (existingCallbackNode !== null) {
      cancelCallback(existingCallbackNode);
    }
    root.callbackPriority = SyncLane;
    root.callbackNode = null;
    return SyncLane;
  } else {
    const existingCallbackPriority = root.callbackPriority;
    // 从 nextLanes 取出当前最高优先级单条车道作为任务优先级
    const newCallbackPriority = getHighestPriorityLane(nextLanes);
    if (
      newCallbackPriority === existingCallbackPriority
    ) {
      return newCallbackPriority;
    } else {
      cancelCallback(existingCallbackNode);
    }
    // 并发 lanes → 通过 Scheduler 调度
    let schedulerPriorityLevel;
    switch (lanesToEventPriority(nextLanes)) {
      // Discrete/Continuous(点击、输入、滚动)
      case DiscreteEventPriority:
      case ContinuousEventPriority:
        // 用户阻塞,优先执行
        schedulerPriorityLevel = UserBlockingSchedulerPriority;
        break;
      case DefaultEventPriority:
        schedulerPriorityLevel = NormalSchedulerPriority;
        break;
      case IdleEventPriority:
        schedulerPriorityLevel = IdleSchedulerPriority;
        break;
    }
    const newCallbackNode = scheduleCallback(
      schedulerPriorityLevel,
      performWorkOnRootViaSchedulerTask.bind(null, root),
    );
    root.callbackPriority = newCallbackPriority;
    root.callbackNode = newCallbackNode;
    return newCallbackPriority;
  }
}

作用

  • 标记饥饿过期车道:遍历 root.pendingLanes,对比当前时间戳,把长期得不到执行、被高优任务持续抢占的饥饿车道标记为过期 expiredLanes。过期车道会被优先处理,防止低优更新永久阻塞
  • 计算当前根需要处理的 nextLanes
    • 开启新版副作用调度,当前根有待执行 useEffect:直接使用 pendingPassiveEffectsLanes,把刷副作用逻辑并入本次渲染调度,不再单独创建独立 Scheduler 任务
    • 否则获取下一个要处理的 lanes
  • 无任务(根无任何待处理更新) / 渲染挂起(当前根正在渲染,因 Suspense 等待数据暂停) / 提交挂起(存在中断未完成的提交流程 - 视图过渡、媒体 suspend):取消现存调度任务,清空根任务标记,返回无车道
  • 车道包含同步 SyncLane,且当前不是服务端预渲染场景,保证事件处理(如 click)的即时反馈
    • 同步渲染统一在微任务末尾, flushSyncWorkAcrossRoots_impl 同步执行,不需要交给 Scheduler 分片调度
    • 取消旧异步任务,标记优先级为 SyncLanecallbackNode 置空
  • 异步并发:对比新旧任务优先级,决定复用、取消重建 Scheduler 任务
    • 优先级相等:复用现有任务,直接返回,不重建
    • 优先级升高:取消旧任务,创建新高优任务抢占
    • 映射 Lane 优先级到 Scheduler 调度优先级
    • 创建新 Scheduler 渲染宏任务,回填 root.callbackNode / root.callbackPriority,返回最高优先级车道

设计意义

  • 并发调度饥饿防护机制:自动识别长期得不到执行的低优车道并标记过期,保证多优先级混合更新不会出现永久阻塞,避免频繁高优交互导致列表 / 异步数据低优更新卡死
  • 渲染与 useEffect 副作用共用一套调度任务,减少独立碎片任务
  • 同优先级多次 setState 复用同一个 Scheduler 任务,避免频繁销毁重建
  • 高优更新自动抢占:新交互高优更新取消低优旧任务,保证交互响应
  • 解耦两层优先级模型 :隔离 React 内部 Lane 车道体系与 Scheduler 调度优先级,便于单独调整调度分片策略

五、performWorkOnRootViaSchedulerTask

Scheduler 渲染任务的顶层入口回调,并发分片渲染的启动函数

ini 复制代码
function performWorkOnRootViaSchedulerTask(root, didTimeout) {
  // 全局存在未完成异步提交流程:视图过渡 View Transition、Suspense 媒体延迟提交、未收尾被动副作用队列
  if (hasPendingCommitEffects()) {
    root.callbackNode = null;
    root.callbackPriority = NoLane;
    return null;
  }

  const originalCallbackNode = root.callbackNode;
  const didFlushPassiveEffects = flushPendingEffectsDelayed();
  if (didFlushPassiveEffects) {
    if (root.callbackNode !== originalCallbackNode) {
      return null;
    }
  }
  const workInProgressRoot = getWorkInProgressRoot();
  const workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes();
  const rootHasPendingCommit =
    root.cancelPendingCommit !== null || root.timeoutHandle !== noTimeout;
  const lanes = getNextLanes(
    root,
    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
    rootHasPendingCommit,
  );
  if (lanes === NoLanes) {
    return null;
  }
  
  const forceSync = !disableSchedulerTimeoutInWorkLoop && didTimeout;
  performWorkOnRoot(root, lanes, forceSync);

  scheduleTaskForRootDuringMicrotask(root, now());
  if (root.callbackNode != null && root.callbackNode === originalCallbackNode) {
    return performWorkOnRootViaSchedulerTask.bind(null, root);
  }
  return null;
}

作用

  • 存在未完成异步提交流程,清空当前根的调度任务标记,直接终止本次渲染任务
  • 统一执行所有被动副作用 useEffect,若执行 useEffect 时内部触发高优先级 setState,会取消当前低优调度任务、新建更高优任务,callbackNode 发生变化,直接 return 终止旧渲染
  • 重新计算最新待渲染车道 nextLanes,合并过期车道、过滤 Suspense 阻塞车道、剔除正在渲染的交错更新,无任何待处理更新则直接结束任务。因为 Scheduler 会把多个回调合并进同一个浏览器宏任务,宏任务执行间隙不会让出微任务队列,在调度创建任务到任务实际执行之间,同步代码可能新增 setState 更新,旧车道数据已失效,必须重新读取最新 root.pendingLanes
  • 区分强制同步 / 分片并发,启动真实渲染循环
  • 渲染完成后重新评估剩余更新,生成续执行任务
    • 复用调度函数更新任务状态
    • 返回绑定自身的函数:Scheduler 会在下一个空闲时间片自动续跑剩余渲染
    • 任务被取消 / 无剩余车道:返回 null,本次渲染任务彻底结束

设计意义

  • 完整可中断分片续跑体系:渲染完成后自动评估剩余更新,通过返回函数实现分片续算;单次时间片仅执行少量渲染工作,主动让出主线程
  • 防低饿死机制 :检测到 Scheduler 分配的时间片耗尽,当前车道已过期,必须一次性完整同步渲染,不可中断让出主线程,避免低优更新永久饥饿

六、flushSyncWorkAcrossRoots_impl

全局批量同步渲染入口,微任务末尾统一执行,专门处理同步车道 SyncLane、手势过渡、popstate 同步 Transition 这类需要同步阻塞渲染的任务

ini 复制代码
function flushSyncWorkAcrossRoots_impl(
  syncTransitionLanes,
  onlyLegacy,
) {
  if (isFlushingWork) {
    return;
  }

  if (!mightHavePendingSyncWork) {
    return;
  }

  let didPerformSomeWork;
  isFlushingWork = true;
  do {
    didPerformSomeWork = false;
    let root = firstScheduledRoot;
    while (root !== null) {
      if (onlyLegacy && (disableLegacyMode || root.tag !== LegacyRoot)) {
      } else {
        if (syncTransitionLanes !== NoLanes) {
          const nextLanes = getNextLanesToFlushSync(root, syncTransitionLanes);
          if (nextLanes !== NoLanes) {
            didPerformSomeWork = true;
            performSyncWorkOnRoot(root, nextLanes);
          }
        } else {
          const workInProgressRoot = getWorkInProgressRoot();
          const workInProgressRootRenderLanes =
            getWorkInProgressRootRenderLanes();
          const rootHasPendingCommit =
            root.cancelPendingCommit !== null ||
            root.timeoutHandle !== noTimeout;
          const nextLanes = getNextLanes(
            root,
            root === workInProgressRoot
              ? workInProgressRootRenderLanes
              : NoLanes,
            rootHasPendingCommit,
          );
          if (
            (includesSyncLane(nextLanes) ||
              (enableGestureTransition && isGestureRender(nextLanes))) &&
            !checkIfRootIsPrerendering(root, nextLanes)
          ) {
            didPerformSomeWork = true;
            performSyncWorkOnRoot(root, nextLanes);
          }
        }
      }
      root = root.next;
    }
  } while (didPerformSomeWork);
  isFlushingWork = false;
}

function performSyncWorkOnRoot(root: FiberRoot, lanes: Lanes) {
  const didFlushPassiveEffects = flushPendingEffects();
  if (didFlushPassiveEffects) {
    return null;
  }
  const forceSync = true;
  performWorkOnRoot(root, lanes, forceSync);
}

作用

  • 全局防重入锁判断 :如果已经在同步渲染中,直接 return,阻断递归重入。同步渲染过程中可能触发 setState 再次进入本函数,锁机制防止无限递归栈溢出,是同步渲染安全屏障
  • 无同步任务直接退出
  • 循环遍历全局调度根链表,批量同步渲染所有符合条件的根
    • 仅处理 legacy 旧根过滤
    • 存在同步过渡车道 syncTransitionLanespopstate 路由跳转),需要同步渲染 useTransition 保证页面滚动位置不丢失:调用getNextLanesToFlushSync 提取当前根绑定的同步过渡车道,存在待渲染过渡车道,调用 performSyncWorkOnRoot 同步阻塞渲染,标记 didPerformSomeWork = true,触发外层 do-while 循环兜底
    • 普通同步车道 / 手势过渡同步渲染:调用通用 getNextLanes 计算当前根待处理车道,当车道包含同步 SyncLane 或手势过渡车道(拖拽、滚动等高优手势)且当前根不是服务端预渲染环境(预渲染强制并发分片,禁止阻塞主线程),同步执行 performSyncWorkOnRoot,标记 didPerformSomeWork = true,触发外层 do-while 循环兜底

设计意义

  • 双层循环闭环 :兜底同步渲染内部新增的同步更新,解决同步 setState 嵌套闭环,一轮循环清空所有同步任务
  • 同步渲染周期全局锁:全程阻止重入,保证同步渲染时序完整,杜绝递归死循环
相关推荐
LaughingZhu5 小时前
智能体经典范式构建:ReAct、Plan-and-Solve 与 Reflection
前端·react.js·前端框架
飞天狗6 小时前
长列表滚动卡顿怎么破:虚拟列表选型 + react-virtuoso 实战调优指南
react.js·性能优化
binbin_529 小时前
React 井字棋教程:用一个小游戏理解组件和状态
前端·react.js·前端框架
张元清10 小时前
React useObjectUrl Hook:预览文件与 Blob,不留内存泄漏(2026)
javascript·react.js
真的想不出名儿14 小时前
Md编辑器整合-react
前端·react.js·编辑器
牛仔只喝牛奶1 天前
多端跨端技术选型:小程序 + H5 + App 的两栈拆分落地
react native·react.js
wear工程师1 天前
setState 到底是不是异步的?这题从 React 16 问到 18,标准答案早就变了
react.js·面试
wanghowie1 天前
LangGraph4j 落地(二)— ReAct 多步工具与 trace 扩展
前端·react.js·前端框架
csj501 天前
前端基础之《React(11)—state声明式》
前端·react.js
张元清1 天前
React useLocalStorage Hook:SSR 安全的持久化状态(2026)
javascript·react.js