返回顶部
首页 > 资讯 > 前端开发 > VUE >React中的任务饥饿行为是什么意思
  • 669
分享到

React中的任务饥饿行为是什么意思

2024-04-02 19:04:59 669人浏览 泡泡鱼
摘要

本篇内容主要讲解“React中的任务饥饿行为是什么意思”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“React中的任务饥饿行为是什么意思”吧!本文是在React

本篇内容主要讲解“React中的任务饥饿行为是什么意思”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“React中的任务饥饿行为是什么意思”吧!

本文是在React中的高优先级任务插队机制基础上的后续延伸,先通过阅读这篇文章了解任务调度执行的整体流程,有助于更快地理解本文所讲的内容。

饥饿问题说到底就是高优先级任务不能毫无底线地打断低优先级任务,一旦低优先级任务过期了,那么他就会被提升到同步优先级去立即执行。如下面的例子:

我点击左面的开始按钮,开始渲染大量DOM节点,完成一次正常的高优先级插队任务:

React中的任务饥饿行为是什么意思

而一旦左侧更新的时候去拖动右侧的元素,并在拖动事件中调用setState记录坐标,介入更高优先级的任务,这个时候,左侧的DOM更新过程会被暂停,不过当我拖动到一定时间的时候,左侧的任务过期了,那它就会提升到同步优先级去立即调度,完成DOM的更新(低优先级任务的lane优先级并没有变,只是任务优先级提高了)。

React中的任务饥饿行为是什么意思

要做到这样,React就必须用一个数据结构去存储pendingLanes中有效的lane它对应的过期时间。另外,还要不断地检查这个lane是否过期。

这就涉及到了任务过期时间的记录 以及 过期任务的检查。

lane模型过期时间的数据结构

完整的pendingLanes有31个二进制位,为了方便举例,我们缩减位数,但道理一样。

例如现在有一个lanes:

0 b 0 0 1 1 0 0 0

那么它对应的过期时间的数据结构就是这样一个数组

[ -1, -1, 4395.2254, 3586.2245, -1, -1, -1 ]

在React过期时间的机制中,-1 为 NoTimestamp

即pendingLanes中每一个1的位对应过期时间数组中一个有意义的时间,过期时间数组会被存到root.expirationTimes字段。这个计算和存取以及判断是否过期的逻辑

是在markStarvedLanesAsExpired函数中,每次有任务要被调度的时候都会调用一次。

记录并检查任务过期时间

在React中的高优先级任务插队机制那篇文章中提到过,ensureRootIsScheduled函数作为统一协调任务调度的角色,它会调用markStarvedLanesAsExpired函数,目的是把当前进来的这个任务的过期时间记录到root.expirationTimes,并检查这个任务是否已经过期,若过期则将它的lane放到root.expiredLanes中。

function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {   // 获取旧任务   const existinGCallbacknode = root.callbackNode;    // 记录任务的过期时间,检查是否有过期任务,有则立即将它放到root.expiredLanes,   // 便于接下来将这个任务以同步模式立即调度   markStarvedLanesAsExpired(root, currentTime);    ...  }

markStarvedLanesAsExpired函数的实现如下:

暂时不需要关注suspendedLanes和pingedLanes

export function markStarvedLanesAsExpired(   root: FiberRoot,   currentTime: number, ): void {   // 获取root.pendingLanes   const pendingLanes = root.pendingLanes;   // suspense相关   const suspendedLanes = root.suspendedLanes;   // suspense的任务被恢复的lanes   const pingedLanes = root.pingedLanes;    // 获取root上已有的过期时间   const expirationTimes = root.expirationTimes;    // 遍历待处理的lanes,检查是否到了过期时间,如果过期,   // 这个更新被视为饥饿状态,并把它的lane放到expiredLanes    let lanes = pendingLanes;   while (lanes > 0) {            const index = pickArbitraryLaneIndex(lanes);     const lane = 1 << index;     // 上边两行的计算过程举例如下:     //   lanes = 0b0000000000000000000000000011100     //   index = 4      //       1 = 0b0000000000000000000000000000001     //  1 << 4 = 0b0000000000000000000000000001000      //    lane = 0b0000000000000000000000000001000      const expirationTime = expirationTimes[index];     if (expirationTime === NoTimestamp) {       // Found a pending lane with no expiration time. If it's not suspended, or       // if it's pinged, assume it's CPU-bound. Compute a new expiration time       // using the current time.       // 发现一个没有过期时间并且待处理的lane,如果它没被挂起,       // 或者被触发了,那么去计算过期时间       if (         (lane & suspendedLanes) === NoLanes ||         (lane & pingedLanes) !== NoLanes       ) {          expirationTimes[index] = computeExpirationTime(lane, currentTime);       }     } else if (expirationTime <= currentTime) {       // This lane expired       // 已经过期,将lane并入到expiredLanes中,实现了将lanes标记为过期       root.expiredLanes |= lane;     }     // 将lane从lanes中删除,每循环一次删除一个,直到lanes清空成0,结束循环     lanes &= ~lane;   } }

通过markStarvedLanesAsExpired的标记,过期任务得以被放到root.expiredLanes中在随后获取任务优先级时,会优先从root.expiredLanes中取值去计算优先级,这时得出的优先级是同步级别,因此走到下面会以同步优先级调度。实现过期任务被立即执行。

function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {   // 获取旧任务   const existingCallbackNode = root.callbackNode;    // 记录任务的过期时间,检查是否有过期任务,有则立即将它放到root.expiredLanes,   // 便于接下来将这个任务以同步模式立即调度   markStarvedLanesAsExpired(root, currentTime);    ...    // 若有任务过期,这里获取到的会是同步优先级   const newCallbackPriority = returnNextLanesPriority();    ...    // 调度一个新任务   let newCallbackNode;   if (newCallbackPriority === SyncLanePriority) {     // 过期任务以同步优先级被调度     newCallbackNode = scheduleSyncCallback(       perfORMSyncWorkOnRoot.bind(null, root),     );   } }

记录并检查任务是否过期

concurrent模式下的任务执行会有时间片的体现,检查并记录任务是否过期就发生在每个时间片结束交还主线程的时候。可以理解成在整个(高优先级)任务的执行期间,

持续调用ensureRootIsScheduled去做这件事,这样一旦发现有过期任务,可以立马调度。

执行任务的函数是performConcurrentWorkOnRoot,一旦因为时间片中断了任务,就会调用ensureRootIsScheduled。

function performConcurrentWorkOnRoot(root) {    ...    // 去执行更新任务的工作循环,一旦超出时间片,则会退出renderRootConcurrent   // 去执行下面的逻辑   let exitStatus = renderRootConcurrent(root, lanes);    ...    // 调用ensureRootIsScheduled去检查有无过期任务,是否需要调度过期任务   ensureRootIsScheduled(root, now());    // 更新任务未完成,return自己,方便Scheduler判断任务完成状态   if (root.callbackNode === originalCallbackNode) {     return performConcurrentWorkOnRoot.bind(null, root);   }   // 否则retutn null,表示任务已经完成,通知Scheduler停止调度   return null; }

performConcurrentWorkOnRoot是被Scheduler持续执行的,这与Scheduler的原理相关,可以移步到我写的一篇长文帮你彻底搞懂React的调度机制原理这篇文章去了解一下,如果暂时不了解也没关系,你只需要知道它会被Scheduler在每一个时间片内都调用一次即可。

一旦时间片中断了任务,那么就会走到下面调用ensureRootIsScheduled。我们可以追问一下时间片下的fiber树构建机制,更深入的理解ensureRootIsScheduled

为什么会在时间片结束的时候调用。

这一切都要从renderRootConcurrent函数说起:

function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {    // workLoopConcurrent中判断超出时间片了,   // 那workLoopConcurrent就会从调用栈弹出,   // 走到下面的break,终止循环    // 然后走到循环下面的代码   // 就说明是被时间片打断任务了,或者fiber树直接构建完了   // 依据情况return不同的status   do {     try {       workLoopConcurrent();       break;     } catch (thrownValue) {       handleError(root, thrownValue);     }   } while (true);     if (workInProgress !== null) {       // workInProgress 不为null,说明是被时间片打断的       // return RootIncomplete说明还没完成任务     return RootIncomplete;   } else {      // 否则说明任务完成了

renderRootConcurrent中写了一个do...while(true)的循环,目的是如果任务执行的时间未超出时间片限制(一般未5ms),那就一直执行,

直到workLoopConcurrent调用完成出栈,brake掉循环。

workLoopConcurrent中依据时间片去深度优先构建fiber树

function workLoopConcurrent() {   // 调用shouldYield判断如果超出时间片限制,那么结束循环   while (workInProgress !== null && !shouldYield()) {     performUnitOfWork(workInProgress);   } }

所以整个持续检查过期任务过程是:一个更新任务被调度,Scheduler调用performConcurrentWorkOnRoot去执行任务,后面的步骤:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. performConcurrentWorkOnRoot调用renderRootConcurrent,renderRootConcurrent去调用workLoopConcurrent执行fiber的构建任务,也就是update引起的更新任务。

  3. 当执行时间超出时间片限制之后,首先workLoopConcurrent会弹出调用栈,然后renderRootConcurrent中的do...while(true)被break掉,使得它也弹出调用栈,因此回到performConcurrentWorkOnRoot中。

  4. performConcurrentWorkOnRoot继续往下执行,调用ensureRootIsScheduled检查有无过期任务需要被调度。

  5. 本次时间片跳出后的逻辑完成,Scheduler会再次调用performConcurrentWorkOnRoot执行任务,重复1到3的过程,也就实现了持续检查过期任务。

总结

低优先级任务的饥饿问题其实本质上还是高优先级任务插队,但是低优先级任务在被长时间的打断之后,它的优先级并没有提高,提高的根本原因是markStarvedLanesAsExpired

将过期任务的优先级放入root.expiredLanes,之后优先从expiredLanes获取任务优先级以及渲染优先级,即使pendingLanes中有更高优先级的任务,但也无法从pendingLanes中

获取到高优任务对应的任务优先级。

到此,相信大家对“React中的任务饥饿行为是什么意思”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: React中的任务饥饿行为是什么意思

本文链接: https://lsjlt.com/news/73755.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • React中的任务饥饿行为是什么意思
    本篇内容主要讲解“React中的任务饥饿行为是什么意思”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“React中的任务饥饿行为是什么意思”吧!本文是在React...
    99+
    2024-04-02
  • Java并发中死锁、活锁和饥饿是什么意思
    解答 死锁是指两个或者两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们将无法推进下去。 如果线程的智力不够, 且都秉承着“谦让”的原则,...
    99+
    2024-04-02
  • Java中导致饥饿的原因是什么
    小编给大家分享一下Java中导致饥饿的原因是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,...
    99+
    2023-05-30
    java
  • react中flux是什么意思
    react中flux是什么意思,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。在react中,flux是一个公共状态管理方案,是...
    99+
    2024-04-02
  • css中的行内式是什么意思
    这篇文章主要介绍css中的行内式是什么意思,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! css行内式指的是使用style属性将css样式代码写在指定的h...
    99+
    2024-04-02
  • React中的任务调度算法是什么
    这篇文章主要讲解了“React中的任务调度算法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“React中的任务调度算法是什么”吧!React中的任务池...
    99+
    2024-04-02
  • border为0在css中是什么意思
    css中border为0表示不显示边框,包括宽度、颜色和样式都设置为0。 border为0在CSS中是什么意思? 在CSS中,border属性用于设置元素的边框。border为0表示不...
    99+
    2024-04-28
    css
  • 华为云服务器是什么意思
    华为云服务器是华为公司开发的云计算服务,它是一个高性能、高可靠性的云计算服务,用于存储、处理和管理海量数据。华为云服务器提供的主要服务包括弹性计算、存储服务、大数据分析、视频云、物联网等。具体来说,华为云服务器提供的功能包括:除此之外,华为...
    99+
    2023-10-25
    华为 服务器
  • javascript中先行断言是什么意思
    本篇内容主要讲解“javascript中先行断言是什么意思”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“javascript中先行断言是什么意思”吧!说明先断言是告诉JavaScript在字符串...
    99+
    2023-06-20
  • php中事务是什么意思
    本文操作环境:Windows10系统、PHP7.1版、Dell G3电脑。php中事务是什么意思事务就是一组原子性的SQL查询,或者说一个独立的工作单元。事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改...
    99+
    2020-06-12
    php
  • MySQL中事务是什么意思
    小编给大家分享一下MySQL中事务是什么意思,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、事务的概念  ...
    99+
    2024-04-02
  • java中的“=”是什么意思?
    赋值运算符“=”:(推荐:java视频教程)作用:将运算符右边的表达式的返回值赋值给左边的变量或者是final 修饰的变量。左边:变量,或者是 final修饰的变量右边:可以是任意表达式(返回的值的类型要和左边的兼容)。=是一个赋值运算符,...
    99+
    2022-01-13
    java
  • java中的“=”是什么意思
    一、对变量而言等于号具有赋值的作用,比如说a=3,就是把3赋值于a。这个是很简单的,大家都经常赋值,除了判断要双等于号外,但单等于号在初始化对象的时候进行赋值。二、对引用对象而言等于号具有传引用(隐式指针)的作用,这个也不难理解,比如说指针...
    99+
    2015-04-04
    java入门 java = 意思
  • datanode中的是什么意思
    这篇文章主要介绍了datanode中的是什么意思,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 DataNode...
    99+
    2024-04-02
  • php中的-=是什么意思
    这篇文章主要讲解了“php中的-=是什么意思”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php中的-=是什么意思”吧!在php中-=符号表示减运算符,该运算符的语法如“x -= y”,该表...
    99+
    2023-06-21
  • MySQL中的/*是什么意思?
    这是一种评论。 是评论的结束。让我们实现并展示如何创建评论mysql> MySQL 将忽略上面的注释。让我们看一个例子。在这里,我们用 编写了注释mysql> mysql> create table DemoTabl...
    99+
    2023-10-22
  • MySQL中的/*是什么意思
    在MySQL中,/ /用于多行注释。它可以在SQL语句中的任何位置使用,用于注释掉一段代码或提供对代码的解释。多行注释可以跨越多个行...
    99+
    2023-10-20
    MySQL
  • c#中的$是什么意思
    c# 中 $ 符号具有以下三个主要含义:1. 字符串内插,用于将表达式嵌入字符串中;2. 匿名类型,用于创建临时类型,其属性与初始化器中的表达式相对应;3. 模式匹配,用于匹配不同的表达...
    99+
    2024-04-04
    c#
  • c#中的+=是什么意思
    c# 中的 += 操作符用于给变量累加值,其语法为 variable += value。它先计算 value 的值,再将其与变量当前值相加,最后将结果重新赋值给变量。 C# 中的 +=...
    99+
    2024-05-10
    c#
  • mysql中的%是什么意思
    mysql 中的 % 通配符用于在 like 操作符中进行模式匹配,表示零个或多个字符。它可用于查找以给定模式开头的所有字符串或包含给定模式的字符串,还可以可选地查找以给定模式结尾的字符...
    99+
    2024-04-26
    mysql
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作