This is the fourth day of my participation in the August More text Challenge. For details, see:August is more challenging

React internal priority management is divided into three types according to different functions: LanePriority, SchedulerPriority, and ReactPriorityLevel. Based on [email protected], this paper combs the priority management system in the source code.

React is a declarative, efficient, and flexible JavaScript library for building user interfaces. The React team has been working on efficient rendering, and there are two very famous presentations:

  1. Lin Clark’s 2017 speechThis paper introduces the infiberThe architecture andInterruptible rendering.
  2. Dan’s 2018 JSConf Speech in IcelandThe time slice (time slicing) and asynchronous rendering (suspense), etc.

Features such as interruptible rendering, time slicing, and asynchronous rendering shown in the talk all depend on priority management to be implemented in the source code.

In the [email protected] source code, there are two priority systems and one conversion system, so let’s review again (the reconciler operation process) before further analysis:

The management of priority in React runs through the four stages of the operation process (from input to output), which can be divided into three types according to its different functions:

  1. fiberPriority (LanePriority) :react-reconcilerBag, that isLane model.
  2. Scheduling priority (SchedulerPriority) :schedulerThe package.
  3. Priority level (ReactPriorityLevel) :react-reconcilerIn the packageSchedulerWithReactIntegration.js, responsible for the conversion of the above two sets of priority systems.

Preliminary knowledge

Before the in-depth analysis of the three priorities, in order to delve into LanePriority, we need to first understand Lane, which is a new feature of [email protected].

Lane model

The English word “lane” means “lane” in Chinese, so many articles refer to “Lanes” model as “lane model”

Lane model source code in reactFiberlane.js, the source code uses a lot of bit operations (about the bit operations, can refer to React algorithm of the position operations).

First, introduce the author’s explanation of Lane (corresponding PR), which is briefly summarized as follows:

  1. Lane types are defined as binary variables that take advantage of the bitmask features to consume less memory and calculate faster in frequent situations.

    • LaneandLanesIs the relationship between the singular and the plural, which represents a single task defined asLane, which represents multiple tasks is defined asLanes
  2. Lane is a refactoring of expirationTime. All columns you would like represented by expirationTime are changed to Lane

      renderExpirationtime -> renderLanes
      update.expirationTime -> update.lane
      fiber.expirationTime -> fiber.lanes
      fiber.childExpirationTime -> fiber.childLanes
      root.firstPendingTime and root.lastPendingTime -> fiber.pendingLanes
    Copy the code
  3. Advantages of Lanes model compared with expirationTime model:

    1. Lanes separate the priority of a task from a batch task, making it easier to determine whether the priority of a single task and a batch task overlap.

      // Determine whether the priority of a single task and a batchTask overlap
      //1. ExpirationTime is expirationTime
      const isTaskIncludedInBatch = priorityOfTask >= priorityOfBatch;
      //2. Judging by Lanes
      constisTaskIncludedInBatch = (task & batchOfTasks) ! = =0;
      
      // When a group of tasks is processed at the same time, there are multiple tasks in the group, and each task has different priorities
      // 1. ExpirationTime = expirationTime = expirationTime = expirationTime = expirationTime = expirationTime A scope needs to be maintained (this is how it was compared in the source code before Lane refactoring)
      const isTaskIncludedInBatch =
        taskPriority <= highestPriorityInRange &&
        taskPriority >= lowestPriorityInRange;
      //2. Judging by Lanes
      constisTaskIncludedInBatch = (task & batchOfTasks) ! = =0;
      Copy the code
    2. Lanes use a single 32-bit binary variable to represent multiple tasks. That is, a variable can represent a group. If you want to separate a single task from a group, you can easily do so.

      Suspense asynchronous rendering didn’t exist in the React system when the expirationTime model was designed. Now we have the following scenario: There are three tasks with priority A > B > C. Normally, you only need to execute them in order of priority. But things are different now: tasks A and C are CPU intensive while B is IO intensive (with remote API calls in Suspense), i.e. A(cup) > B(IO) > C(CPU). In this case, task B should be separated from the group and CPU tasks A and C should be processed first.

      // Delete or add task from group
      
      //1. ExpirationTime
      // 0) Maintain a linked list and insert tasks in order of priority
      // 1) Delete a single task(delete an element from a linked list)
      task.prev.next = task.next;
      // 2) Add a single task to the list.
      let current = queue;
      while (task.expirationTime >= current.expirationTime) {
        current = current.next;
      }
      task.next = current.next;
      current.next = task;
      // 3) Compare whether task is in group
      const isTaskIncludedInBatch =
        taskPriority <= highestPriorityInRange &&
        taskPriority >= lowestPriorityInRange;
      
      // 2. Through Lanes
      // 1) Delete a single task
      batchOfTasks &= ~task;
      // 2) Add a single task
      batchOfTasks |= task;
      // 3) Compare whether task is in group
      constisTaskIncludedInBatch = (task & batchOfTasks) ! = =0;
      Copy the code

      Through the above pseudo code, we can see the advantages of Lanes, which are simple and efficient in using less code.

  4. Lanes are an opaque type and can only be maintained in the reactFiberlane.js module. If you want to use it in other files, you can only use it through the utility functions provided in reactFiberlane.js.

After analyzing the source code of the lane model (in ReactFiberlane.js), the following conclusions can be drawn:

  1. There are 31 bits available (why? Refer to the React algorithm in the position operation description).
  2. A total of defined18 lanes (Lane/Lanes) variable, each variable occupies one or more bits, which are respectively defined asLaneandLanesType.
  3. Each lane (Lane/Lanes), so the source code defines 18 priorities (LanePriority).
  4. Occupying the low bitLaneThe higher the priority of the variable
    • The highest priority isSyncLanePriorityThe corresponding lane isSyncLane = 0b0000000000000000000000000000001.
    • The lowest priority isOffscreenLanePriorityThe corresponding lane isOffscreenLane = 0b1000000000000000000000000000000.

Prioritize distinctions and connections

In the source code, the three priorities are located in different JS files and are independent of each other.

Note:

  • LanePriorityandSchedulerPriorityNominally, they stand forpriority
  • ReactPriorityLevelNominally, it stands forlevelIt’s not a priority, it’s a measureLanePriorityandSchedulerPriorityLevel.

LanePriority

LanePriority: Belongs to the React-Reconciler package, as defined in reactFiberLane.js (see source code).

export const SyncLanePriority: LanePriority = 15;
export const SyncBatchedLanePriority: LanePriority = 14;

const InputDiscreteHydrationLanePriority: LanePriority = 13;
export const InputDiscreteLanePriority: LanePriority = 12;

/ /...

const OffscreenLanePriority: LanePriority = 1;
export const NoLanePriority: LanePriority = 0;
Copy the code

And the related process of fiber structure priority (such as fiber updateQueue, fiber. The lanes) use LanePriority.

Since this section focuses on the priority system and their conversion relationship, the specific use of Lane in the construction of Fiber tree will be explained in detail in the section of Fiber Tree Construction.

SchedulerPriority

SchedulerPriority, which belongs to the Scheduler package, is defined in Schedulerpriorities.js (see source code).

export const NoPriority = 0;
export const ImmediatePriority = 1;
export const UserBlockingPriority = 2;
export const NormalPriority = 3;
export const LowPriority = 4;
export const IdlePriority = 5;
Copy the code

The priority associated with the scheduler scheduling center uses SchedulerPriority.

ReactPriorityLevel

ReactPriorityLevel, belong to react – the reconciler package, defined in SchedulerWithReactIntegration. (see the source code) in js.

export const ImmediatePriority: ReactPriorityLevel = 99;
export const UserBlockingPriority: ReactPriorityLevel = 98;
export const NormalPriority: ReactPriorityLevel = 97;
export const LowPriority: ReactPriorityLevel = 96;
export const IdlePriority: ReactPriorityLevel = 95;
// NoPriority is the absence of priority. Also React-only.
export const NoPriority: ReactPriorityLevel = 90;
Copy the code

LanePriority and SchedulerPriority are converted through ReactPriorityLevel

Conversion relationship

In order to coordinate the use of priorities in scheduler package and Fiber tree structure (React-Reconciler package), SchedulerPriority and LanePriority need to be transformed. The bridge of transformation is ReactPriorityLevel.

In SchedulerWithReactIntegration. Js, SchedulerPriority and ReactPriorityLevel can transfers:

// Convert SchedulerPriority to ReactPriorityLevel
export function getCurrentPriorityLevel() :ReactPriorityLevel {
  switch (Scheduler_getCurrentPriorityLevel()) {
    case Scheduler_ImmediatePriority:
      return ImmediatePriority;
    case Scheduler_UserBlockingPriority:
      return UserBlockingPriority;
    case Scheduler_NormalPriority:
      return NormalPriority;
    case Scheduler_LowPriority:
      return LowPriority;
    case Scheduler_IdlePriority:
      return IdlePriority;
    default:
      invariant(false.'Unknown priority level.'); }}// Convert ReactPriorityLevel to SchedulerPriority
function reactPriorityToSchedulerPriority(reactPriorityLevel) {
  switch (reactPriorityLevel) {
    case ImmediatePriority:
      return Scheduler_ImmediatePriority;
    case UserBlockingPriority:
      return Scheduler_UserBlockingPriority;
    case NormalPriority:
      return Scheduler_NormalPriority;
    case LowPriority:
      return Scheduler_LowPriority;
    case IdlePriority:
      return Scheduler_IdlePriority;
    default:
      invariant(false.'Unknown priority level.'); }}Copy the code

In reactFiberlane.js, you can interconvert LanePriority and ReactPriorityLevel:

export function schedulerPriorityToLanePriority(
  schedulerPriorityLevel: ReactPriorityLevel,
) :LanePriority {
  switch (schedulerPriorityLevel) {
    case ImmediateSchedulerPriority:
      return SyncLanePriority;
    / /... Omit part of the code
    default:
      returnNoLanePriority; }}export function lanePriorityToSchedulerPriority(
  lanePriority: LanePriority,
) :ReactPriorityLevel {
  switch (lanePriority) {
    case SyncLanePriority:
    case SyncBatchedLanePriority:
      return ImmediateSchedulerPriority;
    / /... Omit part of the code
    default:
      invariant(
        false.'Invalid update priority: %s. This is a bug in React.', lanePriority, ); }}Copy the code

Priority usage

Through induction in the reconciler’s operational process, it goes through four stages from input to output, each of which involves processing related to priority. It is through the flexible use of priorities that React implements features such as interruptible rendering, time slicing, and asynchronous rendering.

After understanding the basic idea of priorities, it’s time to officially enter the core part of react source analysis (scheduler scheduling principles and Fiber tree construction).

conclusion

This article introduces the react source code about the priority part, and combed the differences and connections between the three priorities. They run through the four stages of reconciler operation process and occupy a high amount of code in react source code. Understanding their design ideas lays a foundation for the subsequent analysis of scheduling principles and fiber construction.

Write in the last

This article belongs to the diagram react source code series in the operation of the core plate, this series of nearly 20 articles, really in order to understand the React source code, and then improve the architecture and coding ability.

The first draft of the graphic section has been completed and will be updated in August. If there are any errors in the article, we will correct them as soon as possible on Github.