Expiration Time concept
First, what is Expiration Time? According to the English direct translation, the Expiration Time or Expiration Time. How to understand the concept of Expiration Time in React? We might as well start with its role to understand what it is.
Expiration Time action
In real, the source code location is to call computeExpirationForFiber at the updateContainer location in the preparation phase to calculate the time. Here, the update object of real is created in the preparation phase to prepare for the subsequent real scheduling. It represents the point in time at which the task should be executed in the future, otherwise it will expire. See reactfiberexpirationtime in the React reconciler package for details JS specific code content
To sum up: in the process of creating updates, react reasonably arranges the update sequence for subsequent update scheduling. React will set an Expiration Time. When the Expiration Time arrives, it will force the update.
Specific source code content
Because the source code versions are different, there will be little difference. No specific analysis will be made here
export function updateContainer( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?React$Component<any, any>, callback: ?Function, ): ExpirationTime { // Gets the currently updated Fiber node const current = container.current; // Get current time const currentTime = requestCurrentTime(); // Calculate ExpirationTime const expirationTime = computeExpirationForFiber(currentTime, current); return updateContainerAtExpirationTime( element, container, parentComponent, expirationTime, callback, ); } Copy code
How to calculate Expiration Time
First, let's look at the Expiration Time code, which only involves the calculation method
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt'; export type ExpirationTime = number; export const NoWork = 0; export const Sync = 1; export const Never = MAX_SIGNED_31_BIT_INT; const UNIT_SIZE = 10; const MAGIC_NUMBER_OFFSET = 2; // 1 unit of expiration time represents 10ms. export function msToExpirationTime(ms: number): ExpirationTime { // Always add an offset so that we don't clash with the magic number for NoWork. return ((ms / UNIT_SIZE) | 0) + MAGIC_NUMBER_OFFSET; } export function expirationTimeToMs(expirationTime: ExpirationTime): number { return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE; } function ceiling(num: number, precision: number): number { return (((num / precision) | 0) + 1) * precision; } // Core content function computeExpirationBucket( currentTime, expirationInMs, bucketSizeMs, ): ExpirationTime { // currentTime is the current timestamp return ( MAGIC_NUMBER_OFFSET + ceiling( currentTime - MAGIC_NUMBER_OFFSET + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE, ) ); } export const LOW_PRIORITY_EXPIRATION = 5000; export const LOW_PRIORITY_BATCH_SIZE = 250; // Common asynchronous type export function computeAsyncExpiration( currentTime: ExpirationTime, ): ExpirationTime { return computeExpirationBucket( currentTime, LOW_PRIORITY_EXPIRATION, LOW_PRIORITY_BATCH_SIZE, ); } export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150; export const HIGH_PRIORITY_BATCH_SIZE = 100; // Interactive type export function computeInteractiveExpiration(currentTime: ExpirationTime) { return computeExpirationBucket( currentTime, HIGH_PRIORITY_EXPIRATION, HIGH_PRIORITY_BATCH_SIZE, ); } Copy code
When you see the code, you can see two types of Expiration Time. One is ordinary asynchronous and the other is Interactive. For example, if Interactive is triggered by events, its response priority will be higher because it involves interaction.
Ex amp les & core content
Let's take a random type as an example. The currentTime 5000 250 is passed into the computeExpirationBucket. This involves a method. ceiling can be understood as a rounding method. Finally ((((currentTime - 2 + 5000 / 10) / 25) | 0) + 1) * 25 can be obtained, where 25 is 250 / 10 and| 0 is the function of rounding
What is the meaning of the formula? The previous currentTime - 2 + 5000 / 10 is relatively fixed, which is equal to the current time + 498
then ➗ 25 rounded and then ➕ 1 again × five
Finally (current time + 498) ➗ 25 rounded and then ➕ 1 again × five
Add 498 to the current time, then round it by 25, add 1, and multiply it by 5. It should be noted that the currentTime here is processed by msToExpirationTime, that is ((now / 10) | 0) + 2, so the subtraction of 2 here can be ignored, and the rounding by 10 should smooth out the error of 10 milliseconds, Of course, when it is finally used to calculate the time difference, it will call expirationTimeToMs to recover, but the 10 millisecond error removed by rounding must not be recovered
Simply put, here, the final result increases in units of 25. For example, if we input between 10002 and 10026, the final result is 10525, but the result of 10027 is 10550, which is the effect of dividing by 25.
Another thing to mention is the msToExpirationTime and expirationTimeToMs methods. They want to change the transformation relationship. One important thing to note here is that the currentTime used to calculate the expirationTime is obtained through msToExpirationTime(now), that is, it has been processed in advance. First, 10 and then 2 are added. Here 2 is magicNumberOffset, so it can be understood that the later calculation of expirationTime needs to subtract 2
Unit concept
Code first
export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150; export const HIGH_PRIORITY_BATCH_SIZE = 100; export const LOW_PRIORITY_EXPIRATION = 5000; export const LOW_PRIORITY_BATCH_SIZE = 250; Copy code
The 25 mentioned above is a time unit. The expiration time calculated in this time unit is the same. React is to use the same expiration time for the updated contents in the same time unit. In this way, the updates will be merged (there will be opportunities to share later). It is assumed that if there is no unit concept, each call will create an update, There is no priority order, which will waste performance and affect efficiency. In this way, expiration time has priority to facilitate subsequent scheduling updates.
Summary
The design of React is equivalent to smoothing the error of calculating the expiration time within 25ms. The purpose of this is to obtain the same expiration time for two very detailed updates, and then complete it in one update, which is equivalent to an automatic batch update
The above is the calculation method of expirationTime. The following two will share the introduction of each expiration time in the source code
Additional content
In React, we calculate expirationTime based on the current clock time. Generally speaking, we only need to obtain date Now or performance Now yes, but each time you get it, it consumes performance. Therefore, React sets currentRendererTime to record this value for some scenes that do not need to be recalculated.
However, in ReactFiberScheduler, the currentSchedulerTime variable is also provided to record this value. Let's take a look at the implementation of the requestCurrentTime method. You can see why by looking at the comments here. You can directly return the latest time
if (isRendering) { // We're already rendering. Return the most recently read time. return currentSchedulerTime; } Copy code
This isRendering is set to true only when performWorkOnRoot is executed. It is a synchronous method. It does not exist. It jumps out when isRendering is not set to false halfway through execution. Under what circumstances will a new requestCurrentTime appear here?
- The setState method is invoked in the lifecycle method.
- When a task needs to be suspended
if ( nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never ) { // If there's no pending work, or if the pending work is offscreen, we can // read the current time without risk of tearing. recomputeCurrentRendererTime(); currentSchedulerTime = currentRendererTime; return currentSchedulerTime; } Copy code
In other words, in a batch update, the time will be recalculated only when the update is created for the first time, and all subsequent updates will reuse the time when the update is created for the first time. This is also to ensure that the same type of updates generated in a batch update will only have the same expiration time
last
If you think this article is a little helpful to you, give it a compliment. Or you can join my development exchange group: 1025263163 learn from each other, and we will have professional technical Q & A to solve doubts
If you think this article is useful to you, please click star: http://github.crmeb.net/u/defu Thank you very much!
PHP learning manual: https://doc.crmeb.com
Technical exchange forum: https://q.crmeb.com