Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 《React源码》笔记
  • React-reconciler
东流
2026-01-23
目录

ensureRootIsScheduled方法解析

# 概览

每当根节点接收到更新时,都会调用ensureRootIsScheduled函数,它执行两项操作:

  1. 确保根节点的更新在调度中
  2. 确保有一个(挂起的)微任务来处理1中的调度

大部分实际的调度逻辑是在scheduleTaskForRootDuringMicrotask方法运行时才会执行

# 源码解析

# ensureRootIsScheduled

ensureRootIsScheduled方法会根据条件将根节点添加到调度链表,若没有已安排的微任务,则调用scheduleImmediateRootScheduleTask安排新微任务。其源码实现如下:

let firstScheduledRoot = null; // 链表头:第一个待调度的根节点

let lastScheduledRoot = null; // 链表尾:最后一个待调度的根节点

let didScheduleMicrotask = false; // 是否已安排微任务

function ensureRootIsScheduled(root) {
  /**
   * 将根节点添加到调度链表中
   */
  // 判断 若根节点不是最后一个已调度的,且没有在链表中
  if (root !== lastScheduledRoot && root.next === null) {
    // 若链表为空,则将链表的首尾都设置为当前根节点
    if (lastScheduledRoot === null) {
      firstScheduledRoot = lastScheduledRoot = root;
    } else {
      // 链表不为空,则添加到末尾
      lastScheduledRoot.next = root;
      lastScheduledRoot = root;
    }
  }
  // 标记有未完成的同步工作
  mightHavePendingSyncWork = true;
  
  // 如果没有已安排的微任务,则安排新的微任务
  if (!didScheduleMicrotask) {
    didScheduleMicrotask = true;
    scheduleImmediateRootScheduleTask();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# scheduleImmediateRootScheduleTask

scheduleImmediateRootScheduleTask顾名思义就是将根调度安排到一个微任务中去执行。

var  scheduleCallback$3 = Scheduler.unstable_scheduleCallback;

function scheduleImmediateRootScheduleTask() {
  scheduleMicrotask(function () {
    // 检查当前是否处于渲染或提交阶段
    if ((executionContext & (RenderContext | CommitContext)) !== 0) {
      // 若不在这两个阶段,则调用scheduleCallback$3
      scheduleCallback$3(ImmediatePriority, processRootScheduleInImmediateTask);
    } else {
      // 若当前处于渲染或提交阶段,则直接调用processRootScheduleInMicrotask
      processRootScheduleInMicrotask();
    }
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

scheduleCallback$3本质上是调度器scheduler的一个方法,会根据优先级进行调度,后面会讲到。ImmediatePriority表示任务的优先级,是一个高优级,即时更新。

# scheduleMicrotask

而 scheduleMicrotask则是将任务(回调)放在微任务队列中执行,其实现如下:

var scheduleTimeout = "function" === typeof setTimeout ? setTimeout : void 0,
  cancelTimeout = "function" === typeof clearTimeout ? clearTimeout : void 0,
  localPromise = "function" === typeof Promise ? Promise : void 0,
  scheduleMicrotask =
    "function" === typeof queueMicrotask
      ? queueMicrotask
      : "undefined" !== typeof localPromise
      ? function (callback) {
          return localPromise
            .resolve(null)
            .then(callback)
            .catch(handleErrorInNextTick);
        }
      : scheduleTimeout;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

scheduleMicrotask的优先级顺序:

  • 优先使用queueMicrotask(现代浏览器API)
  • 其次使用Promise.then(Promise微任务)
  • 最后回退到setTimeout(宏任务)

这种设计确保了最佳的微任务调度性能。

# processRootScheduleInMicrotask

processRootScheduleInMicrotask方法总是在微任务中被调用,并且从不被同步调用。

function processRootScheduleInMicrotask() {
  // 重置微任务调度标志
  didScheduleMicrotask = false;
  // 重置待同步任务标志
  mightHavePendingSyncWork = false;

  let syncTransitionLanes = NoLanes;
  // 检查当前是否有事件过渡车道
  if (currentEventTransitionLane !== NoLane) {
    // 判断是否应该尝试急切过渡,若需要,则设置同步过渡车道
    if (shouldAttemptEagerTransition()) {
      syncTransitionLanes = currentEventTransitionLane;
    }
  }

  // 链表遍历与调度 
  for (
    var currentTime = now(), prev = null, root = firstScheduledRoot;
    null !== root;
  ) {
    // 为每个根节点调用scheduleTaskForRootDuringMicrotask进行调度任务
    var next = root.next,
      nexLanes = scheduleTaskForRootDuringMicrotask(root, currentTime);
    // 如果返回的车道是NoLane,则表示没有任务需要调度,就需要更新链表,从链表中移除该节点  
    if (nexLanes === NoLane) {
      root.next = null;
      if (prev === null) {
        firstScheduledRoot = next;
      } else {
        prev.next = next;
      }

      if (next === null) {
        lastScheduledRoot = prev;
      }
    } else {
      // 将prev更新为当前根节点
      prev = root;
      // 若有同步多度车道或者返回的车道中包含高优先级车道,则标记有待处理的同步工作
      if (syncTransitionLanes !== NoLanes || NoLanes !== (nexLanes & 3)) {
        mightHavePendingSyncWork = true;
      }
    }
    // 更新当前节点,处理下一个节点
    root = next;
  }

  // 如果存在一个正在commit的工作,则跳过同步刷新工作,否则调用flushSyncWorkAcrossRoots_impl筛选所有同步工作
  (0 !== pendingEffectsStatus && 5 !== pendingEffectsStatus) ||
    flushSyncWorkAcrossRoots_impl(syncTransitionLanes, !1);
  
  // 重置当前事件过渡车道
  0 !== currentEventTransitionLane && (currentEventTransitionLane = 0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# scheduleTaskForRootDuringMicrotask

scheduleTaskForRootDuringMicrotask方法决定了何时、如何以及以什么优先级执行更新,是React并发渲染能力的关键所在。

function scheduleTaskForRootDuringMicrotask(root, currentTime) {
  // 遍历所有待处理车道过期时间
  for (
    var suspendedLanes = root.suspendedLanes,
      pingedLanes = root.pingedLanes,
      expirationTimes = root.expirationTimes,
      lanes = root.pendingLanes & -62914561;
    0 < lanes;

  ) {
    var index$5 = 31 - clz32(lanes),
      lane = 1 << index$5,
      expirationTime = expirationTimes[index$5];
    // 若未设置过期时间  
    if (-1 === expirationTime) {
      // 若车道未被挂起,或者被检测到,则使用当前时间计算一个新的过期时间并赋值给该车道
      if (0 === (lane & suspendedLanes) || 0 !== (lane & pingedLanes))
        expirationTimes[index$5] = computeExpirationTime(lane, currentTime);
    } else expirationTime <= currentTime && (root.expiredLanes |= lane);// 若已过期,则标记为已过期,方便后续处理
    // 清除已处理的车道
    lanes &= ~lane;
  }
  // 保存workInProgressRoot的相关引用
  currentTime = workInProgressRoot;
  suspendedLanes = workInProgressRootRenderLanes;

  // 调用getNextLanes获取下一个要处理的车道
  suspendedLanes = getNextLanes(
    root,
    root === currentTime ? suspendedLanes : 0, // 如果当前根节点是正在工作的根节点,则使用其渲染车道
    null !== root.cancelPendingCommit || -1 !== root.timeoutHandle, // 是否应该包含同步车道,若根节点有取消提交或超时了,则包含
  );
  // 获取当前已调度的回调节点
  pingedLanes = root.callbackNode;

  // 检查是否需要取消调度
  /**
   * 1. 没有要处理的车道
   * 2. 当前根节点是工作根节点并且工作节点被挂起
   * 3. 根节点有取消提交 
   */
  if (
    0 === suspendedLanes ||
    (root === currentTime &&
      (2 === workInProgressSuspendedReason ||
        9 === workInProgressSuspendedReason)) ||
    null !== root.cancelPendingCommit
  ){
      // 取消已处理的回调     
      null !== pingedLanes &&cancelCallback$1(pingedLanes);
      // 重置回调节点和优先级 
      (root.callbackNode = null),
      (root.callbackPriority = 0)

    return NoLanes // 返回0 表示无车道
  }
  // 检查是否应该调度异步任务:车道中不包含同步车道或者根节点处于预渲染状态
  if (
    0 === (suspendedLanes & 3) ||
    checkIfRootIsPrerendering(root, suspendedLanes)
  ) {
    // 获取最高优先级的车道
    currentTime = suspendedLanes & -suspendedLanes;
    // 若优先级与当前回调优先级相同,则直接返回最高优先级车道
    if (currentTime === root.callbackPriority) return currentTime;
    // 取消现有的回调
    null !== pingedLanes && cancelCallback$1(pingedLanes);

    // 将车道转换为调度器优先级
    switch (lanesToEventPriority(suspendedLanes)) {
      case 2:
      case 8:
        suspendedLanes = UserBlockingPriority; //用户阻塞优先级
        break;
      case 32:
        suspendedLanes = NormalPriority$1; // 普通优先级
        break;
      case 268435456:
        suspendedLanes = IdlePriority; // 空闲优先级
        break;
      default:
        suspendedLanes = NormalPriority$1; // 默认为普通优先级
    }
    // 创建回调函数,该回调函数中会调用 performWorkOnRoot
    pingedLanes = performWorkOnRootViaSchedulerTask.bind(null, root);
    // 使用调度器scheduler调度任务
    suspendedLanes = scheduleCallback$3(suspendedLanes, pingedLanes);
    // 更新根节点的回调信息
    root.callbackPriority = currentTime;
    root.callbackNode = suspendedLanes;
    // 返回车道
    return currentTime;
  }

  // 同步任务处理
  // 取消现有的回调
  null !== pingedLanes && cancelCallback$1(pingedLanes);
  // 设置同步优先级
  root.callbackPriority = 2;
  root.callbackNode = null;
  // 返回同步车道
  return 2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

# flushSyncWorkAcrossRoots_impl

flushSyncWorkAcrossRoots_impl方法会筛选同步任务,其伪代码实现如下:

function flushSyncWorkAcrossRoots_impl(){
  if(!isFlushingWork && mightHavePendingSyncWork){
    isFlushingWork = !0;
    do{
      var didPerformSomeWork = !1;
      for(let root = firstScheduleRoot;root!=null;){
         performSyncWorkOnRoot(root, nextLanes);
         root = root.next;
      }
    }while(didPerformSomeWork)
    isFlushingWork = !1; 
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

flushSyncWorkAcrossRoots_impl方法会遍历和刷新所有根节点的同步工作,内部的核心函数就是调用performSyncWorkOnRoot.

# 总结

同步更新的入口是performSyncWorkOnRoot,异步更新入口是performWorkOnRoot,而前者本质上还是调用的performWorkRoot,第三个参数为true。

function performSyncWorkOnRoot(root, lanes) {
  if (flushPendingEffects()) return null;
  performWorkOnRoot(root, lanes, !0);
}
1
2
3
4
编辑 (opens new window)
上次更新: 2026/01/24, 10:05:32
最近更新
01
Scheduler中的最小二叉堆
01-26
02
performWorkOnRoot方法解析
01-24
03
ReactLane模型详解
01-24
更多文章>
Theme by Vdoing | Copyright © 2024-2026 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式