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

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以内,则结果相等
  • 至于 expirationTime 的作用还要结合后期更新的流程来看
  • expirationTime 是一个和业务无关的比较纯粹的计算过程问题,没有任何副作用

相关文章:

  • 机器人制作开源方案 | 乒乓球自动拾取机器人
  • 【数字人】9、DiffTalk | 使用扩散模型基于 audio-driven+对应人物视频 合成说话头(CVPR2023)
  • Python3 中常用字符串函数介绍
  • 秒杀系统如何设计
  • Linux文件
  • Python - 深夜数据结构与算法之 AVL 树 红黑树
  • 案例123:基于微信小程序的在线订餐系统的设计与实现
  • 使用scipy处理图片——旋转任意角度
  • K8S--Ingress的作用
  • 结构化数据,非结构化数据,半结构化数据(AI问答)
  • 数据通信基础
  • gcd得最大公约数,辗转相除法理解
  • Dockerfile镜像实战
  • ReactHooks:渲染与useState
  • openssl3.2 - 在VS2019下源码调试openssl.exe
  • create-react-app项目添加less配置
  • HTTP 简介
  • Java IO学习笔记一
  • Java深入 - 深入理解Java集合
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • LeetCode18.四数之和 JavaScript
  • PHP 7 修改了什么呢 -- 2
  • Spark学习笔记之相关记录
  • Wamp集成环境 添加PHP的新版本
  • webgl (原生)基础入门指南【一】
  • 大数据与云计算学习:数据分析(二)
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 强力优化Rancher k8s中国区的使用体验
  • 深度解析利用ES6进行Promise封装总结
  • 一个SAP顾问在美国的这些年
  • 译米田引理
  • 运行时添加log4j2的appender
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • 《码出高效》学习笔记与书中错误记录
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​queue --- 一个同步的队列类​
  • #define
  • #define,static,const,三种常量的区别
  • #git 撤消对文件的更改
  • #LLM入门|Prompt#3.3_存储_Memory
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (6)设计一个TimeMap
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (Python) SOAP Web Service (HTTP POST)
  • (阿里云万网)-域名注册购买实名流程
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (转)memcache、redis缓存
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .NET/C# 使用反射注册事件
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器