当前位置: 首页 > news >正文

React16源码: React中的不同的expirationTime的源码实现

不同的 expirationTime


1 )概述

  • 在React中不仅仅有异步任务
  • 大部分情况下都是同步的任务,所以会有不同 expirationTime 的存在

2 )种类

  • A. Sync 模式,优先级最高
    • 任务创建完成之后,立马更新到真正的dom里面
    • 是一个创建即更新的流程
  • B. Async 模式, 异步模式
    • 会有一个调度
    • 包含一系列复杂的操作在里面
    • 可能会中断,所以会有一个计算出来的过期时间
    • 过期时间根据异步的两种情况,如前文所述
      • 最低优先级的异步
      • 最高优先级的异步
  • C. 指定context

3 ) 源码

  • 在 ReactFiberReconciler.js 中的 updateContainer
    • 调用的是 computeExpirationForFiber
      • 它接收两个参数: currentTime: ExpirationTime, fiber: Fiber
    • 这个方法来自于 ReactFiberScheduler.js 文件
      function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {let expirationTime;if (expirationContext !== NoWork) {// An explicit expiration context was set;expirationTime = expirationContext;} else if (isWorking) {if (isCommitting) {// Updates that occur during the commit phase should have sync priority// by default.expirationTime = Sync;} else {// Updates during the render phase should expire at the same time as// the work that is being rendered.expirationTime = nextRenderExpirationTime;}} else {// No explicit expiration context was set, and we're not currently// performing work. Calculate a new expiration time.if (fiber.mode & ConcurrentMode) {if (isBatchingInteractiveUpdates) {// This is an interactive updateexpirationTime = computeInteractiveExpiration(currentTime);} else {// This is an async updateexpirationTime = computeAsyncExpiration(currentTime);}// If we're in the middle of rendering a tree, do not update at the same// expiration time that is already rendering.if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {expirationTime += 1;}} else {// This is a sync updateexpirationTime = Sync;}}if (isBatchingInteractiveUpdates) {// This is an interactive update. Keep track of the lowest pending// interactive expiration time. This allows us to synchronously flush// all interactive updates when needed.if (expirationTime > lowestPriorityPendingInteractiveExpirationTime) {lowestPriorityPendingInteractiveExpirationTime = expirationTime;}}return expirationTime;
      }
      
      • 1 )这里有一系列的判断,首先是对 expirationContext 的判断
        • 它是 ExpirationTime 类型,默认初始化值是 NoWork
        • 下面是它的一个修改场景
          function deferredUpdates<A>(fn: () => A): A {const currentTime = requestCurrentTime();const previousExpirationContext = expirationContext;const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;expirationContext = computeAsyncExpiration(currentTime); // 这里isBatchingInteractiveUpdates = false;try {return fn();} finally {expirationContext = previousExpirationContext;isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;}
          }
          
          • expirationContext = computeAsyncExpiration(currentTime);
          • 可以看到 computeAsyncExpiration 这个函数对其进行修改赋值
        • 还有一种场景, 往下进行搜索
          function syncUpdates<A, B, C0, D, R>(fn: (A, B, C0, D) => R,a: A,b: B,c: C0,d: D,
          ): R {const previousExpirationContext = expirationContext;expirationContext = Sync; // 变成了 Synctry {return fn(a, b, c, d); // 这里是 setState 操作} finally {expirationContext = previousExpirationContext; // 最终还原}
          }
          
          • syncUpdates 在 ReactDOM.js 中的 flushSync API
            • flushSync: DOMRenderer.flushSync 一直溯源往上找到 ReactFiberScheduler.js 中的 flushSync
            • 这个就是 本源的 flushSync
              // TODO: Batching should be implemented at the renderer level, not within
              // the reconciler.
              function flushSync<A, R>(fn: (a: A) => R, a: A): R {invariant(!isRendering,'flushSync was called from inside a lifecycle method. It cannot be ' +'called when React is already rendering.',);const previousIsBatchingUpdates = isBatchingUpdates;isBatchingUpdates = true;try {return syncUpdates(fn, a); // 这里} finally {isBatchingUpdates = previousIsBatchingUpdates;performSyncWork();}
              }
              
            • 上述调用的是 syncUpdates,在之前的示例中,如下
              flushSync(() => {this.setState({num: newNum,})
              })
              
            • 上述示例就是传入 fn 回调, 内部调用 setState
            • 在这种场景下 expirationTime 变成了 Sync
        • 以上两种情况 是给 expirationContext 进行赋值
      • 2 )当 expirationTime 变成了 Sync 就不符合第一种情况了,这时候往下走,匹配到了 isWorking
        • isWorking 表示有任务正在更新
        • 也是基于条件指定某个值
        • 这块涉及到后续任务的更新,跳过
      • 3 )没有外部强制的情况下
        • if (fiber.mode & ConcurrentMode)
        • 判断 fiber.mode 是否是 ConcurrentMode
        • 找到 ConcurrentMode 的定义处 ReactTypeOfMode.js
          export type TypeOfMode = number;export const NoContext = 0b000;
          export const ConcurrentMode = 0b001;
          export const StrictMode = 0b010;
          export const ProfileMode = 0b100;
          
        • 可见,使用二进制方式来定义的
        • 可以通过 与或 这类逻辑的操作,非常方便的组合 Mode
        • 以及判断是否有某个Mode,例如
          const a = 0b000;
          const b = 0b001;
          const c = 0b010;
          const d = 0b100;
          let mode = a; // 默认等于 a, 在后续渲染时并不知道是否有更改过
          mode & b // 如果 结果为 0 表示没有过b这种情况
          mode |= b // 这样给 mode 增加 b,这时候 mode 变成1,就对应了 b
          mode |= c // 给 mode 增加 c, 这时候 mode 变成 3,也就是 0b011
          mode & b // 这时候判断mode是否有b, 如果是1,则有b, 结果是1 对应b
          mode & c // 这时候判断mode是否有c, 如果是1,则有c, 结果是2 对应c
          
          • 这是一种巧妙的设计方式
          • 类似的,在 ReactSideEffectTags.js 中也是这么设计的
        • 回到代码里,如果是 ConcurrentMode 才会调用
        • computeInteractiveExpirationcomputeAsyncExpiration 两个函数中的一个计算 expirationTime
          if (isBatchingInteractiveUpdates) {// This is an interactive updateexpirationTime = computeInteractiveExpiration(currentTime);
          } else {// This is an async updateexpirationTime = computeAsyncExpiration(currentTime);
          }
          
          • 关于 isBatchingInteractiveUpdatesinteractiveUpdates 函数中被赋值为 true
            • 大部分的 React 事件产生的更新,事件中绑定回调函数,这个回调函数执行的时候
            • 大部分都是在 interactiveUpdates 情况下,也就是 isBatchingInteractiveUpdates 为 true 时
          • 后面 expirationTime += 1; 是为了区分下一个即将进行的更新和当前正在创造的更新,防止一样,强制把当前+1
        • 如果不属于,则还是 Sync

相关文章:

React16源码: React中的不同的expirationTime的源码实现

不同的 expirationTime 1 &#xff09;概述 在React中不仅仅有异步任务大部分情况下都是同步的任务&#xff0c;所以会有不同 expirationTime 的存在 2 &#xff09;种类 A. Sync 模式&#xff0c;优先级最高 任务创建完成之后&#xff0c;立马更新到真正的dom里面是一个创建…...

TRB 2024论文分享:基于生成对抗网络和Transformer模型的交通事件检测混合模型

TRB&#xff08;Transportation Research Board&#xff0c;美国交通研究委员会&#xff0c;简称TRB&#xff09;会议是交通研究领域知名度最高学术会议之一&#xff0c;近年来的参会人数已经超过了2万名&#xff0c;是参与人数和国家最多的学术盛会。TRB会议几乎涵盖了交通领域…...

Golang 打包

构建/打包 使用 Go 的构建命令&#xff1a; go build在包含 main 函数的包的目录下执行&#xff0c;它会生成一个可执行文件。文件名默认与包所在的目录名相同&#xff0c;但也可以使用 -o 选项来指定输出的文件名 交叉编译 Windows 环境下进行交叉编译以构建其他平台的可执…...

力扣每日一练(24-1-14)

做过类似的题&#xff0c;一眼就是双指针&#xff0c;刚好也就是题解。 if not nums:return 0p1 0 for p2 in range(1, len(nums)):if nums[p2] ! nums[p1]:p1 1nums[p1] nums[p2]return p1 1 根据规律&#xff0c;重复的数字必定相连&#xff0c;那么只要下一个数字与上一…...

K 个一组翻转链表(链表反转,固定长度反转)(困难)

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你链表的头节点head&#xff0c;每k个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是k的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。…...

Spring Boot - 利用Resilience4j-RateLimiter进行流量控制和服务降级

文章目录 Resilience4j概述Resilience4j官方地址Resilience4j-RateLimiter微服务演示Payment processorPOM配置文件ServiceController Payment servicePOMModelServiceRestConfigController配置验证 探究 Rate Limiting请求三次 &#xff0c;观察等待15秒连续访问6次 Resilienc…...

概率论与数理统计————1.随机事件与概率

一、随机事件 随机试验&#xff1a;满足三个特点 &#xff08;1&#xff09;可重复性&#xff1a;可在相同的条件下重复进行 &#xff08;2&#xff09;可预知性&#xff1a;每次试验的可能不止一个&#xff0c;事先知道试验的所有可能结果 &#xff08;3&#xff09;不确定…...

【生存技能】git操作

先下载git https://git-scm.com/downloads 我这里是win64&#xff0c;下载了相应的直接安装版本 64-bit Git for Windows Setup 打开git bash 设置用户名和邮箱 查看设置的配置信息 获取本地仓库 在git bash或powershell执行git init&#xff0c;初始化当前目录成为git仓库…...

docker 将镜像打包为 tar 包

目录 1 实现 1 实现 要将镜像导出为.tar包&#xff0c;可以使用Docker命令行工具进行操作。下面是导出镜像的步骤&#xff1a; 首先&#xff0c;使用以下命令列出当前系统上的镜像&#xff0c;并找到要导出的镜像的ID或名称&#xff1a; docker images使用以下命令将镜像导出为…...

341. 最优贸易(dp思想运用,spfa,最短路)

341. 最优贸易 - AcWing题库 C 国有 n 个大城市和 m 条道路&#xff0c;每条道路连接这 n 个城市中的某两个城市。 任意两个城市之间最多只有一条道路直接相连。 这 m 条道路中有一部分为单向通行的道路&#xff0c;一部分为双向通行的道路&#xff0c;双向通行的道路在统计…...

FineBI实战项目一(19):每小时订单笔数分析开发

点击新建组件&#xff0c;创建下每小时订单笔数组件。 选择饼图&#xff0c;拖拽cnt&#xff08;总数&#xff09;到角度&#xff0c;拖拽hourstr到颜色&#xff0c;调节内径。 修改现在的文字 拖拽组件到仪表盘。 效果如下&#xff1a;...

What is `@RequestBody ` does?

RequestBody 是SpringMVC框架中的注解&#xff0c;通常与POST、PUT等方法配合使用。当客户端发送包含JSON或XML格式数据的请求时&#xff0c;可以通过该注解将请求体内容绑定到Controller方法参数上 作用 自动反序列化&#xff1a; SpringMVC会根据RequestBody注解的参数类型&…...

Windows安装Rust环境(详细教程)

一、 安装mingw64(C语言环境) Rust默认使用的C语言依赖Visual Studio&#xff0c;但该工具占用空间大安装也较为麻烦&#xff0c;可以选用轻便的mingw64包。 1.1 安装地址 (1) 下载地址1-GitHub&#xff1a;Releases niXman/mingw-builds-binaries GitHub (2) 下载地址2-W…...

Marin说PCB之传输线损耗---趋肤效应和导体损耗01

大家在做RF上的PCB走线或者是车载相机的上走线的时候经常会听那些硬件工程师们说你这个走线一定要保证50欧姆的阻抗匹配啊&#xff0c;还有就是记得加粗走做隔层参考。 有的公司的EE硬件同事会很贴心的把RF走线的注意事项给你备注在原理图上或者是layoutguide上&#xff0c;遇到…...

八:分布式锁

1、为什么要使用分布式锁 锁是多线程代码中的概念&#xff0c;只有多任务访问同一个互斥的共享资源时才需要锁。单机应用开发时一般使用synchronized或lock。多线程的运行都是在同一个JVM之下。应用是分布式集群&#xff0c;属于多JVM的工作环境&#xff0c;JVM之间已经无法通过…...

示例:php将文本内容写入一个文件(面向过程写法)

一、封装2个函数&#xff0c;读写文件 /*** desc 读取文件内容* param string $filename* return array*/ private function readContent(string $filename): array {$text file_get_contents($filename);if (!$text) {return [];}$result json_decode($text,true);return…...

Flutter开发进阶之并发操作数据库

Flutter开发进阶之并发操作数据库 尽管 Flutter 本身不包含任何数据库功能&#xff0c;但可以使用各种第三方库和插件来在 Flutter 应用程序中实现数据库功能&#xff1b; 以下将使用sqflite作为例子&#xff0c;sqflite允许在 Flutter 应用程序中执行 SQL 查询&#xff0c;创…...

docker应用:搭建uptime-kuma监控站点

简介&#xff1a;Uptime Kuma是一个易于使用的自托管监控工具&#xff0c;它的界面干净简洁&#xff0c;部署和使用都非常方便。 历史攻略&#xff1a; docker&#xff1a;可视化工具portainer docker-compose&#xff1a;搭建自动化运维平台Spug 开源地址&#xff1a; ht…...

在illustrator中按大小尺寸选择物体 <脚本 018>

在Illustrator中我们可以依据对象的属性 如&#xff1a;填充颜色、描边颜色或描边宽度来选择相同属性的对象&#xff0c;但是Illustrator中没有根据不同大小尺寸来选择对象的功能&#xff0c;下面介绍的就是根据大小尺寸选择对象的脚本。 1、下面是当前画板中的所有对象&#…...

leetcode - 934. Shortest Bridge

Description You are given an n x n binary matrix grid where 1 represents land and 0 represents water. An island is a 4-directionally connected group of 1’s not connected to any other 1’s. There are exactly two islands in grid. You may change 0’s to 1…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

算术操作符与类型转换:从基础到精通

目录 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符&#xff1a;、-、*、/、% 赋值操作符&#xff1a;和复合赋值 单⽬操作符&#xff1a;、--、、- 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...

算法250609 高精度

加法 #include<stdio.h> #include<iostream> #include<string.h> #include<math.h> #include<algorithm> using namespace std; char input1[205]; char input2[205]; int main(){while(scanf("%s%s",input1,input2)!EOF){int a[205]…...

轻量安全的密码管理工具Vaultwarden

一、Vaultwarden概述 Vaultwarden主要作用是提供一个自托管的密码管理器服务。它是Bitwarden密码管理器的第三方轻量版&#xff0c;由国外开发者在Bitwarden的基础上&#xff0c;采用Rust语言重写而成。 &#xff08;一&#xff09;Vaultwarden镜像的作用及特点 轻量级与高性…...