React16源码: React中更新阶段中不同类型的expirationTime之pendingTime,suspendedTime以及pingedTime的源码实现

React-从源码分析React Fiber工作原理 本文的demo仓库在https://github.com/qiqingjin/blog/tree/master/React_Redux,喜欢请star哟~ 为什么要重写React React16 以前 React16 以前,对virtural dom的更新和渲染是同步的。就是当一次更新或者一次加载开始以后,diff virtual dom并且渲染的过程是一口气完成的。如果组件... 阅读详情

不同类型的 expirationTime


1 )概述

  • 在 react 中有几种不同类型的 expirationTime
    • pendingTime
    • suspendedTime
    • pingedTime

2 )源码

2.1 关于 pendingTime

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L1788

function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
  const root = scheduleWorkToRoot(fiber, expirationTime);
  // ... 跳过很多代码
  markPendingPriorityLevel(root, expirationTime); // 注意这里
  // ... 跳过很多代码
}
  • 所有创建更新都会通过调用 scheduleWork 来开始进行一个任务队列的调度的一个过程
  • 因为对于调用 scheduleWork 进来的任务,都认为它是 pending 的任务
  • pending 的任务就是一个等待渲染的一个任务, 这个任务因为没有经过任何的更新,没有经过像 suspend 这样的流程
  • 所以它是一个新创建的任务,是一个pending的任务

定位到 packages/react-reconciler/src/ReactFiberPendingPriority.js#L19

进入 markPendingPriorityLevel

export function markPendingPriorityLevel(
  root: FiberRoot,
  expirationTime: ExpirationTime,
): void {
  // If there's a gap between completing a failed root and retrying it,
  // additional updates may be scheduled. Clear `didError`, in case the update
  // is sufficient to fix the error.
  root.didError = false;

  // Update the latest and earliest pending times
  const earliestPendingTime = root.earliestPendingTime;
  if (earliestPendingTime === NoWork) {
    // No other pending updates.
    root.earliestPendingTime = root.latestPendingTime = expirationTime;
  } else {
    if (earliestPendingTime < expirationTime) {
      // This is the earliest pending update.
      root.earliestPendingTime = expirationTime;
    } else {
      const latestPendingTime = root.latestPendingTime;
      if (latestPendingTime > expirationTime) {
        // This is the latest pending update
        root.latestPendingTime = expirationTime;
      }
    }
  }
  findNextExpirationTimeToWorkOn(expirationTime, root); // 该函数用于确定下一个要处理的过期时间
}
  • 获取 root.earliestPendingTime
  • 如果 earliestPendingTime === NoWork 代表现在root上面没有正在等待更新的任务
    • 这时候 root.earliestPendingTime = root.latestPendingTime = expirationTime
    • 因为它是第一个
  • 如果有 earliestPendingTime 而且 earliestPendingTime < expirationTime
    • earliestPendingTime 表示最早的等待更新时间,取最大值,优先级最小
    • root.earliestPendingTime = expirationTime;
  • 如果 latestPendingTime > expirationTime
    • latestPendingTime 表示最晚的等待更新时间,取最小值, 优先级最大
    • root.latestPendingTime = expirationTime;
  • 之后,调用这个 findNextExpirationTimeToWorkOn 方法

2.2 关于 suspendedTime

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L1433

下面是 renderRoot 中的一段代码

注意这里的 markSuspendedPriorityLevel

if (nextRenderDidError) {
  // There was an error
  if (hasLowerPriorityWork(root, expirationTime)) {
    // There's lower priority work. If so, it may have the effect of fixing
    // the exception that was just thrown. Exit without committing. This is
    // similar to a suspend, but without a timeout because we're not waiting
    // for a promise to resolve. React will restart at the lower
    // priority level.
    markSuspendedPriorityLevel(root, expirationTime); // 注意这里
    
    // ... 跳过很多代码

    return;
  } else if (
    // There's no lower priority work, but we're rendering asynchronously.
    // Synchronsouly attempt to render the same level one more time. This is
    // similar to a suspend, but without a timeout because we're not waiting
    // for a promise to resolve.
    !root.didError &&
    isYieldy
  ) {
    // ... 跳过很多代码
  }
}
  • hasLowerPriorityWork 表示对于有低优先级的任务,这时候调用 markSuspendedPriorityLevel, 进入它
    export function markSuspendedPriorityLevel(
      root: FiberRoot,
      suspendedTime: ExpirationTime,
    ): void {
      root.didError = false;
      clearPing(root, suspendedTime);
    
      // First, check the known pending levels and update them if needed.
      // 这俩值是用于记录 root 上面所有被挂起的任务,它的 expirationTime 的区间的
      const earliestPendingTime = root.earliestPendingTime;
      const latestPendingTime = root.latestPendingTime;
      if (earliestPendingTime === suspendedTime) {
        if (latestPendingTime === suspendedTime) {
          // Both known pending levels were suspended. Clear them.
          root.earliestPendingTime = root.latestPendingTime = NoWork;
        } else {
          // The earliest pending level was suspended. Clear by setting it to the
          // latest pending level.
          root.earliestPendingTime = latestPendingTime;
        }
      } else if (latestPendingTime === suspendedTime) {
        // The latest pending level was suspended. Clear by setting it to the
        // latest pending level.
        root.latestPendingTime = earliestPendingTime; // 这里是清理的工作,后续有新的 expirationTime 进来后进行新的设置
      }
    
      // 接下去才是真正的操作
      // Finally, update the known suspended levels.
      const earliestSuspendedTime = root.earliestSuspendedTime;
      const latestSuspendedTime = root.latestSuspendedTime;
      if (earliestSuspendedTime === NoWork) {
        // No other suspended levels.
        root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
      } else {
        if (earliestSuspendedTime < suspendedTime) {
          // This is the earliest suspended level.
          root.earliestSuspendedTime = suspendedTime;
        } else if (latestSuspendedTime > suspendedTime) {
          // This is the latest suspended level
          root.latestSuspendedTime = suspendedTime;
        }
      }
    
      findNextExpirationTimeToWorkOn(suspendedTime, root);
    }
    
    • 这个方法跟设置 pendingTime 的方法其实是差不多的
    • 这里不进行逐个解释, 主要功能是处理被挂起任务的 expirationTime 最早和最晚的区间的

2.3 pingedTime

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L1656

在 retrySuspendedRoot 函数内执行了 markPingedPriorityLevel

function retrySuspendedRoot(
  root: FiberRoot,
  boundaryFiber: Fiber,
  sourceFiber: Fiber,
  suspendedTime: ExpirationTime,
) {
  let retryTime;

  if (isPriorityLevelSuspended(root, suspendedTime)) {
    // Ping at the original level
    retryTime = suspendedTime;

    markPingedPriorityLevel(root, retryTime); // 注意这里
  } else {
    // ... 跳过很多代码
  }

  // ... 跳过很多代码
}
  • 这个 retrySuspendedRoot 函数在 throw 了 Promise,并且resolve之后会调用这个方法

进入 markPingedPriorityLevel

export function markPingedPriorityLevel(
  root: FiberRoot,
  pingedTime: ExpirationTime,
): void {
  root.didError = false;

  // TODO: When we add back resuming, we need to ensure the progressed work
  // is thrown out and not reused during the restarted render. One way to
  // invalidate the progressed work is to restart at expirationTime + 1.
  const latestPingedTime = root.latestPingedTime;
  if (latestPingedTime === NoWork || latestPingedTime > pingedTime) {
    root.latestPingedTime = pingedTime; // 这里取最小值
  }
  findNextExpirationTimeToWorkOn(pingedTime, root);
}

现在发现,3种 expirationTime 的处理最终都调用了 findNextExpirationTimeToWorkOn

// packages/react-reconciler/src/ReactFiberPendingPriority.js#L248

function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
  const earliestSuspendedTime = root.earliestSuspendedTime;
  const latestSuspendedTime = root.latestSuspendedTime;
  const earliestPendingTime = root.earliestPendingTime;
  const latestPingedTime = root.latestPingedTime;

  // Work on the earliest pending time. Failing that, work on the latest
  // pinged time.
  // 对于 nextExpirationTimeToWorkOn 的赋值,存在 earliestPendingTime 则取其值,否则取 latestPingedTime 值
  let nextExpirationTimeToWorkOn =
    earliestPendingTime !== NoWork ? earliestPendingTime : latestPingedTime;

  // If there is no pending or pinged work, check if there's suspended work
  // that's lower priority than what we just completed.
  // 这时候还要判断下和 suspend 的关系
  // nextExpirationTimeToWorkOn === NoWork 代表了 earliestPendingTime 和 latestPingedTime 都是 NoWork
  if (
    nextExpirationTimeToWorkOn === NoWork &&
    (completedExpirationTime === NoWork ||
      latestSuspendedTime < completedExpirationTime)
  ) {
    // The lowest priority suspended work is the work most likely to be
    // committed next. Let's start rendering it again, so that if it times out,
    // it's ready to commit.
    nextExpirationTimeToWorkOn = latestSuspendedTime; // 设置优先级最高的
  }
  
  // 接下去是去设置 expirationTime
  let expirationTime = nextExpirationTimeToWorkOn;
  if (expirationTime !== NoWork && earliestSuspendedTime > expirationTime) {
    // Expire using the earliest known expiration time.
    expirationTime = earliestSuspendedTime;
  }

  root.nextExpirationTimeToWorkOn = nextExpirationTimeToWorkOn;
  root.expirationTime = expirationTime;
}
  • 这里可以看到 expirationTime 和 nextExpirationTimeToWorkOn 的区别在于
    • 如果有 earliestSuspendedTime 并且比 expirationTime 要大, 即 earliestSuspendedTime的优先级较低
      • 则 expirationTime 取 earliestSuspendedTime,取优先级低的
    • 还有,就是比如在 renderRoot 里面,手动设置 root.expirationTime = Sync 来发起一个同步的更新任务
      • 这就是 expirationTime 的作用
  • 综上,目前大致了解了在root上面有这三种不同的值来记录不同场景产生的更新中相关 time 的一个范围
unity C# 计算3D空间任意多边形面积,距离,角度测量工具 unity C# 计算3D空间任意多边形面积,距离,角度 using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; using System; /// /// 划线面积,距离,角度 /// public class UnderlinedMeasureTool : MonoBehaviour { /// /// 相机 /// public Camera _c 阅读详情

相关推荐

RV1126平台JD9366触摸屏驱动移植实战:从源码到烧录的完整指南

本文详细介绍了在RV1126平台上移植JD9366触摸屏驱动的完整流程,从环境准备、源码获取到驱动配置、内核编译、设备树设置以及固件烧录与测试验证。通过实战经验分享和常见问题排查,帮助开发者高效完成触摸屏驱动移植,适用于嵌入式Linux开发场景。

weixin_30872337的博客 410

一文吃透 React Expiration Time

Expiration Time 概念 首先 Expiration Time 到底是什么呢? 根据英文直接翻译可知,到期时间或者过期时间。在React中到期时间概念又如何理解,我们不妨从它的作用入手理解到底是什么概念。 Expiration Time 作用 在 React 中,源码位置是在 准备阶段 updateContainer 的位置 调用 computeExpirationForFiber 计算时间,这里是在准备阶段创建好React的更新对象,为后面的后面 React 调度做准备。它代表的是 任务在未来

开源世界 629

RANSAC地面点分割:从原理到C++实战优化

本文深入解析RANSAC算法在点云地面分割中的原理与C++实战优化,涵盖动态迭代次数、自适应阈值等关键技巧,通过并行计算和内存访问优化提升性能,适用于激光雷达点云处理。

weixin_29015051的博客 278

React 源码讲解第 6 节- expirationTime 公式

本节主要讲解 expirationTime 时间计算公式。

杏子 1368

LM Studio中文版零基础入门:从安装到模型下载的保姆级教程(附国内镜像源配置)

本文提供LM Studio中文版的零基础入门教程,涵盖从软件安装、中文界面切换到国内镜像源配置的全流程。特别针对模型下载速度慢的问题,详细介绍了如何通过修改配置文件使用国内镜像站,并推荐适合新手的入门模型及硬件配置建议,帮助用户快速上手本地大模型运行。

pz890123456的博客 2607

Halcon: 获取仿射矩形-rectangle2的四个顶点、四条边的中点

Halcon 工业应用实用教程中的一个例子: * CenterY : 仿射矩形中心y * CenterX: 仿射矩形中心x * Phi: 仿射矩形角度 * Len1: 仿射矩形宽的一半 * Len2: 仿射矩形高的一半 * CornerY: 仿射矩形端点y * CornerX: 仿射矩形端点x * LineCenterY: 仿射矩形边中点y * LineCenterx: 仿射矩形边中点x * 矩...

ruotianxia的博客 8575

React16源码: React中ReactFiberScheduler的expirationTime及nextExpirationTimeToWorkOn的源码实现

expirationTime 作用在渲染之前的,而 nextExpirationTimeToWorkOn 则是作用在渲染时的。让它在下一次执行更新之前来使用 performSyncWork 这种方式来通过同步的方式去渲染任务。这两个值在整个 render 以及 commit 的过程当中,都起着非常重要的一个作用。所以这就是这两个值它的作用, 通过指定它们来指定更新模式以及更新任务的优先级。为什么说 expirationTime 是作用在渲染前的呢?比如说, 在 renderRoot 中。

Wang的专栏 1274

ADS入门学习笔记——低噪声放大器(2)

1、输入匹配网络; 2、输出匹配网络;

weixin_42126696的博客 4991

react 按钮倒计时5秒之后可进行点击操作

react 按钮倒计时5秒可进行点击操作

m0_52743009的博客 1400

基于java实现的弹幕视频网站

开发语言:Java框架:ssm技术:JSPJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9系统展示弹幕视频网站,在弹幕视频网站可以查看首页、视频信息、商品信息、论坛信息、我的、跳转到后台、购物车、客服等内容前台首页界面图登录,通过登录填写账号、密码等信息进行登录操作登录界面图视频信息界面图。

DC5621的博客 1333

Eclipse邂逅Deepseek:本地化部署全攻略

通过前面一系列的步骤,我们成功地在 Eclipse 中完成了 Deepseek 的本地化部署,并且对部署后的功能进行了测试和优化,还解决了可能遇到的一些常见问题。这一过程不仅让我们体验到了将强大的大语言模型与高效的开发环境相结合的魅力,也为我们在软件开发过程中带来了诸多便利。本地化部署 Deepseek 到 Eclipse,使得我们在代码编写过程中能够获得智能的代码补全和精准的问答支持,大大提高了开发效率。同时,本地化部署保障了数据的安全性和隐私性,避免了数据在传输过程中可能面临的风险。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 4523

React16源码: React中的expirationTime过期时间的计算源码实现

先看expirationTime相关的源代码,这里是异步的计算方式,它会有一个过期时间,异步任务优先级比较低,可以被打断,防止一直被打断导致不能执行,所以React给它设置了 expirationTime 过期时间,也就是在这个时间之前,都可以打断,但是如果某个时间点发现任务已经过期了,还没有被执行,则强制执行该任务

Wang的专栏 1851

模型预测控制(MPC)解析(二):单输入单输出系统

在上一篇中,为了便于说明,预测控制系统采用单输入单输出系统为例进行设计。由于系统模型使用的是状态空间方程,于是可以很容易的将这种设计方法可以扩展到多输入、多输出系统。

行者无疆的博客 8225

react源码阅读4 ExpirationTime

react更新中优先级依赖的标识ExpirationTime。阅读React包的源码版本为16.8.6。   这一章节,让我们抛弃掉react代码中的联系,单纯的来看ExpirationTime以及一些计算方式。 ExpirationTime是什么。   ExpirationTime是一个数字,你可以在react-reconciler包下的ReactFiberExpirationTime.js文件中找到它的定义。 export type ExpirationTime = number; Expiratio

tangding12的博客 524

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

react、vue - 解决移动端点击事件300毫秒延迟问题(fastclick.js)

从点击屏幕上的元素到触发元素的点击(click)事件的过程中,移动端浏览器大约有300毫秒的等待时间,主要是想看后面是否还会再做点击操作(是否要进行双击操作),而fastclick.js就是为了解决这个问题。下面我来说一下使用方法: 1、fastclick在react上的使用方式: (1)安装: npm install --save fastclick (2)使用:(写在全局的index.js文件中,直接作用于整个项目) import React from 'react'; import ReactDOM

qq_41547882的博客 1074

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

react源码浅析(六):到期时间的计算规则

NodeNote,持续更新中react相关库源码浅析, react ts3 项目 [TOC] 到期时间的计算方法 到期时间算法源码位置: react\packages\react-reconciler\src\ReactFiberExpirationTime.js 复制代码摘要: react主要把到期时间分为两种:异步任务到期时间与交互动作的到期时间。在这之前需要了解一下一些重要的函数,reac...

weixin_33948416的博客 1218

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

React 使用总结:setState回调 + 设置延时 + promise.then(onCompleted, onRejected) +.....

1.React setState方法回调的写法: 因为js是异步的,所以有时候会用到setState的回调方法,其作用是在setState()执行完后再调用回调方法: this.setState({ days: daysLimit }, () => { console.log("可选日期范围是:" + this.state.days); }); 如果不考虑异步,直接输出th...

qijingpei的博客 1万+

React中实现定时操作和时间间隔内循环操作

componentDidMount = () =&gt; { // this.autoHiddenConfirm(); } componentWillReceiveProps = (props) =&gt; { if (props.confirmType == 'tips' &amp;&amp; !this.timer) { //...

hahahhahahahha123456的博客 3977

react 等待某个函数执行完_【第1964期】从React Hooks看React的本质

前言昨天React官网挂了一句提示:Black Lives Matter. Support the Equal Justice Initiative.今日早读文章由@canonical授权分享。@canonical,某公司技术总监,毕业于清华大学工程物理系,专业造轮子二十年,企图在软件自动化生产领域复用所学的理论物理知识正文从这开始~~后jQuery时代的前端革命是由AngularJs发...

weixin_39631899的博客 724

Fix signatures do not match the previously installed version

报错信息FAILURE: Build failed with an exception.* What went wrong: Execution failed for task ':app:installDebug'. > com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Fai

Wang的专栏 2万+

在react中 实现 base64的图片转换成formdata格式并实现上传功能

如果不想要服务端实现处理base64格式的图片的转换,那么在浏览器端来实现,部分属于伪代码,仅作为开发参考,请勿直接拷贝使用

Wang的专栏 8836

快速解决gradle下载慢的问题的小技巧

在ionic run 或build 或clean命令的时候,如果没有下载过gradle, 需要重新下载一次,而通过CLI的方式下载很慢,即使科学上网后,下载还是存在一些问题。这里总结一下,快速的下载安装方式。 步骤 本地安装 http-server 包, 通过 $ npm i -g http-server 的方式安装到全局 在官网上下载自己的版本,目前自带的版本是 2.14.1,其他版本可能在执行过

Wang的专栏 6714

React 实现 2D 画板功能

在移动端或者PC端我们来模拟画板的实现, 支持清除和导出功能,此处简单实现,仅作开发参考。 效果演示 主要代码演示 /* 此模块用于实现签名绘图功能 */ // 需要用到的变量定义 let clickX = new Array(); let clickY = new Array(); let clickDrag = new Array(); let paint; let point...

Wang的专栏 4325

React16源码: React中LegacyContext的源码实现

对于在 beginWork 中的 context 操作,可以看上述判断成立的条件下的代码,即便有符合跳过更新的操作,依然 需要 push 和 pop 操作。如果我们可以跳出组件的更新,也就是代表着当前这个 classComponent,它的state它的props都应该是没有任何变化的。对于子组件来说,它的所有子树所获取到的context肯定是经过子组件,和上层的父组件合并的 context 了, 也就是。所以,先 push 一个老的值进去再说, 然后到后面,如果发现这个组件它是要更新的,就调用这个。

Wang的专栏 2803

Fix babel-preset-react-native TransformError

错误环境新版本存在不兼容,在运行 react-native run-android 的时候,发生的错误分析: babel-preset-react-native version : @3.0.0 $ react-native -v :react-native-cli: 2.0.1 react-native: 0.47.1$node -v : v7.8.0 $ npm -v :4.4.4 解决方案

Wang的专栏 2800

React18原理: 再聊Fiber架构下的时间分片

react的任务可以被打断,其实就是基于时间分片的,人眼最高能识别的帧数不超过30帧,电影的帧数差不多是在24,浏览器的帧率一般来说是60帧,也就是每秒60个画面, 平均一个画面大概是16.5毫秒左右,浏览器正常的工作流程是运算渲染,运算,渲染运算渲染,在浏览器里面一个运算,加上一个渲染就是一帧

Wang的专栏 2796
上一篇: React16源码: React中详解在渲染阶段Suspend的源码实现
下一篇: React16源码: React中ReactFiberScheduler的expirationTime及nextExpirationTimeToWorkOn的源码实现
Wang's Blog
博客等级 码龄12年 7434粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值