React16源码: React中的expirationTime过期时间的计算源码实现
expirationTime 的计算方式
- 先看expirationTime相关的源代码,这里是异步的计算方式,它会有一个过期时间
- 异步任务优先级比较低,可以被打断,防止一直被打断导致不能执行,所以React给它设置了 expirationTime 过期时间
- 也就是在这个时间之前,都可以打断,但是如果某个时间点发现任务已经过期了,还没有被执行,则强制执行该任务
- 在
ReactDOM.render当中,它计算 expirationTime 的地方- 在 ReactFiberReconciler.js 中的
updateContainer函数中,通过computeExpirationForFiber方法来计算一个过期时间const current = container.current; // 参数2 const currentTime = requestCurrentTime(); // 参数1 我们可以近似理解为: 当前时间到js加载完成的时间的时间差值即可 const expirationTime = computeExpirationForFiber(currentTime, current); - 而
requestCurrentTime这个函数,来自于 ReactFiberScheduler.js 中function requestCurrentTime() {// 这里,我把官方注释移除// 这里表示 已经进入到 渲染阶段 了,在 ReactDOM.render 中这里不会匹配,会跳过// 在一次render中,如果我有一个新的任务进来了,要计算 expirationTime 发现现在处于渲染阶段,这时直接返回上次 render 开始的时间,再去计算 expirationTime// 好处是 前后两次计算出来的 expirationTime 是一样的,让这个任务提前进行调度if (isRendering) {// We're already rendering. Return the most recently read time.return currentSchedulerTime;}// Check if there's pending work.findHighestPriorityRoot();// 刚初始化的时候,这个条件是成立的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;}// 这里,我把官方注释移除return currentSchedulerTime; }findHighestPriorityRoot方法涉及到从调度队列中找到权限最高的 Root- 这个源码比较多,不做扩展
recomputeCurrentRendererTime每一次做计算都是从当前到js加载完成后的时间间隔,再经过一些计算得到的值,function recomputeCurrentRendererTime() {const currentTimeMs = now() - originalStartTimeMs; // 当前时间 - react buddle加载完成之后初始的时间,也就是从js加载完成到现在的时间间隔currentRendererTime = msToExpirationTime(currentTimeMs); // 计算出 currentRendererTime }// ReactFiberExpirationTime.js 这个函数得到一个时间戳 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; // UNIT_SIZE 是固定的 10, | 0 是取整的意思, MAGIC_NUMBER_OFFSET 是固定的 2 }
- 关于
expirationTime的计算函数computeExpirationForFiber有一个计算公式 - 在这个计算时间中,不需要考虑调度,只考虑计算公式,在 ReactFiberExpirationTime.js 中
function ceiling(num: number, precision: number): number {return (((num / precision) | 0) + 1) * precision; }function computeExpirationBucket(currentTime,expirationInMs,bucketSizeMs, ): ExpirationTime {return (MAGIC_NUMBER_OFFSET +ceiling(currentTime - MAGIC_NUMBER_OFFSET + expirationInMs / UNIT_SIZE,bucketSizeMs / UNIT_SIZE,)); }// 低权限计算 export function computeAsyncExpiration(currentTime: ExpirationTime, ): ExpirationTime {return computeExpirationBucket(currentTime,LOW_PRIORITY_EXPIRATION, // 5000LOW_PRIORITY_BATCH_SIZE, // 250); }// 高权限计算 export function computeInteractiveExpiration(currentTime: ExpirationTime) {return computeExpirationBucket(currentTime,HIGH_PRIORITY_EXPIRATION, // 500/150 前 DEV, 后 PRODHIGH_PRIORITY_BATCH_SIZE, // 100); }- 上面两个 export 方法,都是调用
computeExpirationBucket方法来计算的 - 两个方法的区别在于 后面第2和第3个参数是不一样的
- 最终得到的公式是:
((((currentTime - 2 + 5000 / 10) / 25) | 0 ) + 1) * 25- 其中 25 = 250 / 10,上述公式中的25,也可能是 10 (100 / 10)
- 最终计算出来的
expirationTime是以bucketSize / UNIT_SIZE这个单元向上叠加的 - 两个 不同的
expirationTime的差距是 单元值的 倍数 - 对于
LOW_PRIORITY_BATCH_SIZE是 以 25 为单元向上加的, 若前后差距在25以内,计算出来的差距都是一样的 - 对于
HIGH_PRIORITY_BATCH_SIZE是 以 10 为单元向上加的,同上 - React 这么设定的原因: 在计算
expirationTime时 - 在一个操作内多次调用
setState, 即便前后调用差距很小,但从毫秒级别看,还是有差距的 - 如果没有提供任何一个调整空间,即便上个
setState和 下一个setState之间差距特别小,算出来的expirationTime结果不一样 - 这就意味着,两次的任务优先级不一样, 会导致 React 整体更新执行多次,而导致整个应用的性能下降,这就是 设置 单元值的 用处
- 在一个差距很小的时间间隔内,算出来的
expirationTime结果一样,则它们优先级也是一样的,而不需要进行区分 - 这个非常重要
- 最终计算出来的
| 0表示 去余取整- 其中 (currentTime - 2 + 5000 / 10) 是一个不会变化的值,设为 x
- 后面就是 (((x / 25) | 0) + 1) * 25
- 这个公式的意义在于,新老值之间的差距在25以内,则结果相等
- 其中 25 = 250 / 10,上述公式中的25,也可能是 10 (100 / 10)
- 上面两个 export 方法,都是调用
- 在 ReactFiberReconciler.js 中的
- 至于
expirationTime的作用还要结合后期更新的流程来看 expirationTime是一个和业务无关的比较纯粹的计算过程问题,没有任何副作用
相关文章:
React16源码: React中的expirationTime过期时间的计算源码实现
expirationTime 的计算方式 先看expirationTime相关的源代码,这里是异步的计算方式,它会有一个过期时间异步任务优先级比较低,可以被打断,防止一直被打断导致不能执行,所以React给它设置了 expirationTime 过期时间也…...
程序设计语言的分类
编译与解释 编译型 将源代码转换成目标代码,通常源代码是高级语言代码,目标代码是机器语言代码,执行编译的计算机程序称为编译器。 eg:java 好处:对于相同的源代码编译产生的目标代码执行速度更快,目标代码不需要编译…...
Python轻松实现炫酷的手势检测
大家好,今天分享一个非常有意思且十分简单的python库——mediapipe库。该库集成了大量的深度学习模型,短短几行代码,就可以快速实现一个炫酷的实例,本文就以手势检测为例,展示一下这个强大的开源库。 mediapipe由Goog…...
什么是信噪比
大家好,今天给大家介绍什么是信噪比,文章末尾附有分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!可进群免费领取。 “信噪比”是电子技术中经常用到的一个词组,知道它的确切含义有一定意…...
学习redis有效期和数据类型
1、安装redis和连接redis 参考:ubuntu安装单个redis服务_ubuntu redis单机版安装-CSDN博客 连接redis:redis-cli.exe -h localhost -p 6379 -a 123456 2、Redis数据类型 以下操作我们在图形化界面演示。 2.1、五种常用数据类型介绍 Redis存储的是key…...
【linux】进程管理
前言 linux也有类似于windows的任务管理器的功能,我们也可以通过这个功能查看当前的进程情况。 语法 ps [-e] [-f] -e显示所有进程 -f显示完整的信息 我们可以直接用-ef来简化指令。 案例演示 信息过滤 但是如果我们直接这么输入的话,可以看到他回复…...
k8s operator从0到1实践
文章目录 环境准备一个k8s集群开发工具包mac安装 实践初始化operator项目核心逻辑编写测试验证验证 部署 参考 环境准备 一个k8s集群 推荐使用docker-desktop,本地单机集群 开发工具包 这里推荐使用脚手架工具kubebuilder 使用脚手架工具,能生成项目…...
【动态规划】dp多状态问题
欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:【LeetCode】winter vacation training 目录 👉🏻按摩师👉&…...
docker安裝gocd-server,并配置gitlab授权登录
gocd的地址:Installing GoCD server on Windows | GoCD User Documentation gocd文档:GitHub - gocd/docker-gocd-server: Docker server image for GoCD 一、docker拉取gocd镜像 #拉取server镜像 docker pull gocd/gocd-server:v21.1.0docker pull g…...
使用pygame实现简单的烟花效果
import pygame import sys import random import math# 初始化 Pygame pygame.init()# 设置窗口大小 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption("Fireworks Explosion")# 定义颜色 black (0, 0, 0) wh…...
ubantu系统运维命令,端口相关操作
1、使用sudo ufw status命令查看所有开放的端口,如下图: 2、使用命令sudo ufw allow 8443,打开端口8443.如下图: 3、使用 sudo ufw reload刷新端口配置,如下图:...
Java中的Stream API进阶使用
Java的Stream API是Java 8引入的一个强大的功能,它允许以声明性方式处理数据集合,例如过滤、映射、排序等。下面是一些Stream API的进阶使用: 自定义中间操作:你可以定义自己的中间操作,然后在Stream上使用它。例如&am…...
R语言【paleobioDB】——pbdb_collection():从PBDB获取单个采集号的基本信息
Package paleobioDB version 0.7.0 paleobioDB 包在2020年已经停止更新,该包依赖PBDB v1 API。 可以选择在Index of /src/contrib/Archive/paleobioDB (r-project.org)下载安装包后,执行本地安装。 Usage pbdb_collection (id, ...) Arguments 参数【…...
阿里云服务器的tcp端口无法访问(云服务厂家问题?)
问题->无法访问 阿里云服务器的tcp端口 最近一台阿里云服务器的一个端口61616无法访问,在服务器内用外网地ip发现无法访问,用内网ip访问是正常的,通过技术排查: 解决->无法访问 阿里云服务器的tcp端口 1 配置官网的安全组…...
BikeDNA(九) 特征匹配
BikeDNA(九) 特征匹配 特征匹配采用参考数据并尝试识别 OSM 数据集中的相应特征。 特征匹配是比较单个特征而不是研究区域网格单元水平上的特征特征的必要前提。 方法 将两个道路数据集中的特征与其数字化特征的方式以及边缘之间潜在的一对多关系进行…...
vuex是什么?怎么使用?哪种功能场景使用它?
Vuex是Vue.js官方推荐的状态管理库,用于在Vue应用程序中管理和共享状态。它基于Flux架构和单向数据流的概念,将应用程序的状态集中管理,使得状态的变化更可追踪、更易于管理。Vuex提供了一个全局的状态树,以及一些用于修改状态的方…...
求斐波那契数列矩阵乘法的方法
斐波那契数列 先来简单介绍一下斐波那契数列: 斐波那契数列是指这样一个数列:1,1,2,3,5,8,13,21,34,55,89……这个数列从第3项开始 &…...
【IPC通信--消息队列】
消息队列(也叫做报文队列)是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息…...
读写分离的手段——主从复制,解决读流量大大高于写流量的问题
应用场景 假设说有这么一种业务场景,读流量显著高于写流量,你要怎么优化呢。因为写是要加锁的,可能就会阻塞你读请求。而且其实读多写少的场景还很多见,比如电商平台,用户浏览n多个商品才会买一个。 大部分人的思路可…...
Day02
今日任务: 977 有序数组的平方209 长度最小的子数组59 螺旋矩阵Ⅱ 977 有序数组的平方 题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/ 双指针问题,以及数组本身时有序的; 思路: 左、右两个…...
OpenClaw极简开发:用nanobot镜像快速验证自动化脚本
OpenClaw极简开发:用nanobot镜像快速验证自动化脚本 1. 为什么选择nanobot镜像进行OpenClaw开发 作为一名长期在本地折腾AI自动化脚本的开发者,我深知环境配置的痛。每次换机器重装OpenClaw,总要在Node.js版本、Python依赖和模型部署之间反…...
YOLOE零样本迁移实战案例:从LVIS预训练模型快速适配安防监控场景
YOLOE零样本迁移实战案例:从LVIS预训练模型快速适配安防监控场景 1. 引言:当通用模型遇见专业场景 想象一下,你手里有一个能识别上千种物体的“全能”AI模型,现在需要它去盯监控,专门找“可疑人员”、“遗留包裹”和…...
Graphviz自动排版太随机?教你5个技巧精准控制节点位置
Graphviz自动排版太随机?5个专业技巧精准控制节点位置 当你用Graphviz绘制关系图时,是否遇到过这样的困扰:明明代码逻辑清晰,生成的图表却总是不按预期排列?节点位置随机跳跃,关键元素错位,甚至…...
从‘社交网络’到‘路径规划’:邻接表DFS在5个真实场景中的实战应用
从‘社交网络’到‘路径规划’:邻接表DFS在5个真实场景中的实战应用 邻接表和深度优先搜索(DFS)这对黄金组合,远不止是算法教材里的抽象概念。当它们走出理论课本,进入真实世界的复杂系统时,展现出的问题解…...
PyFluent:3大核心场景实现CFD仿真全流程自动化
PyFluent:3大核心场景实现CFD仿真全流程自动化 【免费下载链接】pyfluent 项目地址: https://gitcode.com/gh_mirrors/pyf/pyfluent 计算流体动力学(CFD)仿真作为工程设计的关键环节,长期面临流程繁琐、迭代低效、跨学科协…...
SOONet实战教程:结合Whisper提取音频文本,构建音视频联合语义定位Pipeline
SOONet实战教程:结合Whisper提取音频文本,构建音视频联合语义定位Pipeline 1. 项目概述 今天给大家介绍一个特别实用的技术方案:如何用SOONet视频时序定位系统,结合Whisper语音识别,构建一个完整的音视频语义定位pip…...
MGeo中文地址结构化教程:从原始文本到标准GeoJSON格式输出的完整转换流程
MGeo中文地址结构化教程:从原始文本到标准GeoJSON格式输出的完整转换流程 1. 引言:为什么我们需要地址结构化? 你有没有遇到过这样的场景?用户填写的收货地址五花八门:“北京市海淀区中关村大街27号”、“北京海淀中…...
【部署】windows下虚拟机OpenClaw Ubuntu 24.04.4 安装指南
未来已来,只需一句指令,养龙虾专栏导航,持续更新ing… 概述 前置环境:win10/11、vmware等虚拟机(安装时注意勾选VMware Tools、cpu可以分配2C,内存建议4G,硬盘空间建议给40G) 系统要求 Node.js 22+:安装脚本可自动检测并安装(下文补充手动安装方案); Ubuntu 24.0…...
PostgreSQL权限管理实操:Homebrew安装后,如何正确创建postgres用户并导入项目数据
PostgreSQL权限管理实战:从Homebrew安装到项目数据迁移全指南 当你用Homebrew完成PostgreSQL安装后,真正的挑战才刚刚开始。许多开发者卡在权限配置这一关,导致后续数据迁移和日常操作频频受阻。本文将带你深入PostgreSQL的权限体系ÿ…...
CTE、临时表、子查询如何选?
在 SQL Server 等关系型数据库中,处理复杂查询逻辑时,子查询 (Subquery)、临时表 (Temporary Table) 和公共表表达式 (CTE, Common Table Expression) 是三种核心工具。它们各有优劣,选择哪种取决于具体的性能需求、数据规模、代码可读性以及…...
