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:
- Lin Clark’s 2017 speechThis paper introduces the in
fiber
The architecture andInterruptible rendering
. - 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:
fiber
Priority (LanePriority
) :react-reconciler
Bag, that isLane model
.- Scheduling priority (
SchedulerPriority
) :scheduler
The package. - Priority level (
ReactPriorityLevel
) :react-reconciler
In 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:
-
Lane types are defined as binary variables that take advantage of the bitmask features to consume less memory and calculate faster in frequent situations.
Lane
andLanes
Is the relationship between the singular and the plural, which represents a single task defined asLane
, which represents multiple tasks is defined asLanes
-
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
-
Advantages of Lanes model compared with expirationTime model:
-
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
-
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.
-
-
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:
- There are 31 bits available (why? Refer to the React algorithm in the position operation description).
- A total of defined18 lanes (
Lane/Lanes
) variable, each variable occupies one or more bits, which are respectively defined asLane
andLanes
Type. - Each lane (
Lane/Lanes
), so the source code defines 18 priorities (LanePriority). - Occupying the low bit
Lane
The higher the priority of the variable- The highest priority is
SyncLanePriority
The corresponding lane isSyncLane = 0b0000000000000000000000000000001
. - The lowest priority is
OffscreenLanePriority
The corresponding lane isOffscreenLane = 0b1000000000000000000000000000000
.
- The highest priority is
Prioritize distinctions and connections
In the source code, the three priorities are located in different JS files and are independent of each other.
Note:
LanePriority
andSchedulerPriority
Nominally, they stand forpriority
ReactPriorityLevel
Nominally, it stands forlevel
It’s not a priority, it’s a measureLanePriority
andSchedulerPriority
Level.
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.