React 源码系列:分析 ensureRootIsScheduled(root, currentTime)

1,241 阅读3分钟

「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

在前面的文章提到了scheduleUpdateOnFiber函数会调用 ensureRootIsScheduled , 显然后者包含了与渲染更新有关的重要逻辑,今天来分析一下这个函数做了哪些东西。

功能

这是用于为一个 root 调度一个任务,每个 root 只能有一个任务。如果任务已经被调度完成,则会进行一个 check, 以确保处于 existing 状态的任务的优先级与 root 的下一层的优先级相同。此函数会在每次更新时被调用,退出调用之前会给 root.callbackroot.callbackPriority 重新赋值。

车道与优先级

ensureRootIsScheduled的代码可以意识到,我们上一篇文章提到的“车道”(lane)概念,其实是用于实现一种优先级机制。

  const newCallbackPriority = getHighestPriorityLane(nextLanes)

每一个车道都表示一个优先级。这里截一张图,看一下各个车道具体的取值如何: image.png 每一个车道在二进制第 i 位上为 1,表示优先级为 i,i 越小,优先级越高。如果有多个二进制位是1,表示一组车道,有多个优先级。

为什么认为 i 越小,优先级越高?

export function getHighestPriorityLane (lanes: Lanes): Lane {
  return lanes & -lanes
}

getHighestPriorityLane 从名字上看就知道它是获取最高优先级的 lane 的,而 lanes & -lanes 的作用是返回一个数,最低非 0 位保留为 1,其它位设为 0。举个例子,0b110 & -0b110 的结果为 2,即 0b010

这里可以多提一句,我们又是怎么看出 lanes & -lanes的运算结果是这样呢?培训过 acm 的人想必很敏感了,这是因为在计算机中一个负数是用补码表示的,补码等于反码加 1,这个结果又可以看成从最高位开始往低位扫描,直到遇到最低非 0 位,按位取反。因此一个数与它的负数做位与运算,结果自然是保留最低非0位,其它位为 0 了。

实现逻辑

  1. 调用 markStarvedLanesAsExpired(root, currentTime),用于将已经被其他工作“饿死”的lane标记为已过期,后面会避免使用已过期的 lane
  2. 调用 getNextLanes 方法获取下一次要用的车道组,如果获取不到可用车道,即调用结果是 NoLanes, 则退出。退出前执行 root.callbackNode = nullroot.callbackPriority = NoLane
  3. 如果当前调度的任务是是一个 Scheduler 任务而不是一个“act”任务,则退出,重新调度“act”的队列
  4. 如果是开发模式或 ReactCurrentActQueue.current 为 null 或 root.callbackNode 是 fakeActCallbackNode,则退出
  5. root.callbackNode如果存在,则执行 cancelCallback (root.callbackNode)
  6. 如果车道组中的最高优先级车道是SyncLane,则调用 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root))scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root)),调用哪个取决于root.tag
  7. 如果支持微任务,则执行 scheduleMicrotask(flushSyncCallbacks),否则执行 scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks)
  8. root.callbackNode 置 null
  9. 如果 6 不成立,则调lanesToEventPriority(nextLanes),根据返回情况给临时变量schedulerPriorityLevel分配不同的取值,然后执行以下代码
root.callbackNode = scheduleCallback(
     schedulerPriorityLevel,
     performConcurrentWorkOnRoot.bind(null, root),
   )

下节预告

上一节里我们又看到一些新的名词,markStarvedLanesAsExpiredcancelCallbackscheduleCallbackscheduleSyncCallbackscheduleMicrotask等,这些重要的概念将在下一次更文中讲解。