React18源码: React调度中的3种优先级类型和Lane的位运算

优先级类型

  • React内部对于优先级的管理,贯穿运作流程的4个阶段(从输入到输出),根据其功能的不同,可以分为3种类型:
    • 1 )fiber优先级(LanePriority)
      • 位于 react-reconciler包,也就是Lane(车道模型)
    • 2 )调度优先级(SchedulerPriority)
      • 位于scheduler包
    • 3 )优先级等级(ReactPriorityLevel)
      • 位于react-reconciler包中的 SchedulerWithReactIntegration.js
      • 负责上述2套优先级体系的转换.
  • Lane 是在 react@17.0.0的新特性.

Lane(车道模型)

  • 英文单词lane翻译成中文表示"车道,航道"的意思,所以很多文章都将Lanes模型称为车道模型

  • Lane模型的源码在 ReactFiberLane.js,源码中大量使用了位运算

  • 首先引入对Lane的解释, 这里简单概括如下:

    • 1 )Lane类型被定义为二进制变量,利用了位掩码的特性,在频繁运算的时候占用内存少,计算速度快.
      • Lane和Lanes就是单数和复数的关系,代表单个任务的定义为Lane,代表多个任务的定义为Lanes
    • 2 )Lane是对于expirationTime的重构,以前使用expirationTime表示的字段,都改为了lane
      renderExpirationTime -> renderlanes
      update.expirationTime -> update.lane
      fiber.expirationTime -> fiber.lanes
      fiber.childExpirationTime -> fiber.childLanes
      root.firstPendingTime and root.lastPendingTime -> fiber.pendingLanes
      
    • 3 )使用Lanes模型相比expirationTime模型的优势
      • Lanes把任务优先级从批量任务中分离出来
      • 可以更方便的判断单个任务与批量任务的优先级是否重叠
      // 判断:单task与batchTask的优先级是否重叠
      // 1.通过expirationTime判断
      const isTaskIncludedInBatch = priorityOfTask >= priorityOfBatch;
      // 2.通过Lanes判断
      const isTaskIncludedInBatch =(task & batchOfTasks) !== 0;// 当同时处理一组任务,该组内有多个任务,且每个任务的优先级不一致
      // 1. 如果通过expirationTime判断,需要维护一个范围(在Lane重构之前,源码中就是这样比较的)
      const isTaskIncludedInBatch =taskPriority <= highestPriorityInRange &&taskPriority >= lowestPriorityInRange;
      // 2. 通过Lanes判断
      const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
      
  • Lanes使用单个32位二进制变量即可代表多个不同的任务

  • 也就是说一个变量即可代表一个组(group)

  • 如果要在一个group中分离出单个task,非常容易

  • 在expirationTime模型设计之初,react体系中还没有 Suspense 异步渲染的概念

  • 现在有如下场景:有3个任务,其优先级A>B>C,正常来讲只需要按照优先级顺序执行就可以了

  • 但是现在情况变了:A和C任务是CPU密集型,而B是IO密集型(Suspense会调用远程api,算是IO任务)

  • 即A(cpu)>B(IO)>C(cpu).此时的需求需要将任务B从group中分离出来,先处理cpu任务A和C

    //从group中删除或增加task// 通过expirationTime实现
    // 维护一个链表,按照单个task的优先级顺序进行插入
    // 删除单个task(从链表中删除一个元素)
    task. prev.next = task.next;
    //2)增加单个task(需要对比当前task的优先级,插入到链表正确的位置上)
    let current = queue;
    while (task.expirationTime >= current.expirationTime) {current = current.next;
    }
    task.next = current.next;
    current.next = task;
    //3)比较task是否在group中
    const isTaskIncludedInBatch =taskPriority <= highestPriorityInRange &&taskPriority >= lowestPriorityInRang;
    //2.通过Lanes实现
    //1)删除单个task
    batchOfTasks &= ~task;
    //2)增加单个task
    batchOfTasks |= task;
    //3)比较task是否在group中
    const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
    
  • Lanes是一个不透明的类型,只能在ReactFiberLane.js这个模块中维护

  • 如果要在其他文件中使用,只能通过 ReactFiberLane.js 中提供的工具函数来使用

  • 分析车道模型的源码(ReactFiberLane.js中),可以得到如下结论:

    • 1.可以使用的比特位一共有31位
    • 2.共定义了18种车道(Lane/Lanes)变量,每一个变量占有1个或多个比特位,分别定义为Lane和Lanes类型.
    • 3.每一种车道(Lane/Lanes)都有对应的优先级,所以源码中定义了18种优先级(LanePriority).
    • 4.占有低位比特位的Lane变量对应的优先级越高
      • 最高优先级为 SynclanePriority 对应的车道为
        • Synclane = 0b0000000000000000000000000000001
      • 最低优先级为 OffscreenLanePriority 对应的车道为
        • OffscreenLane = 0b1000000000000000000000000000000

位运算

  • 什么是位运算?程序中的所有数在计算机内存中都是以二进制的形式储存的。

  • 位运算就是直接对整数在内存中的二进制位进行操作。

  • 比如

    • 0 在二进制中用 0 表示,我们用 0000 代表;
    • 1 在二进制中用 1 表示,我们用 0001 代表;
  • 那么先看两个位运算符号 & 和 |

    • & 对于每一个比特位,两个操作数都为 1 时,结果为 1,否则为 0
    • | 对于每一个比特位,两个操作数都为0时,结果为 0,否则为 1
    • 我们看一下两个 1 & 0 和 1 | 0
    • 如上 1 & 0 = 0, 1 | 0 = 1
  • 参考

      0       00001       0001-------------0 & 1 = 0000 = 0
    
  • 再参考

    0       0000
    1       0001
    ----------------
    0 | 1 = 0001 = 1
    

使用一张表格详细说明

运算符用法描述
与 &a & b如果两位都是1则设置每位为1
或 | a | b 如果两位之一为1则设置每位为1
异或 ^a^b如果两位只有一位为1则设置每位为1
非 ~~ a反转操作数的比特位, 即0变成1, 1变成0
左移(<<)a << b 将a的二进制形式向左移b(< 32)比特位, 右边用0填充
有符号右移(>>)a>>b将a的二进制形式向右移b(<32)比特位,丢弃被移除的位,左侧以最高位来填充
无符号右移(>>>)a>>>b将a的二进制形式向右移b(<32)比特位,丢弃被移除的位,并用0在左侧填充

位运算的一个使用场景

  • 比如有一个场景下,会有很多状态常量A,B,C…,这些状态在整个应用中在一些关键节点中做流程控制
  • 比如:
    if(value === A) {// TODO..
    }
    
  • 如上判断value等于常量A,那么进入到if的条件语句中
  • 此时是value属性是简单的一对一关系,但是实际场景下value可能是好几个枚举常量的集合
  • 也就是一对多的关系,那么此时value可能同时代表A和B两个属性
  • 如下图所示:
  • 这时候,如果按照下面的代码来写,就会很麻烦
    if(value === A || value === B) {// TODO..
    }
    
  • 此时的问题就是如何用一个value表示A和B两个属性的集合,这个时候位运算就派上用场了
  • 因为可以把一些状态常量用32位的二进制来表示(这里也可以用其他进制),比如:
    const A = 0b0000000000000000000000000000001 // 优先级最高
    const B = 0b0000000000000000000000000000010
    const C = 0b0000000000000000000000000000100 // 优先级最低
    
  • 通过移位的方式让每一个常量都单独占一位,这样在判断一个属性是否包含常量的时候
  • 可以根据当前位数的1和0来判断
  • 这样如果一个值即代表A又代表B那么就可以通过位运算的 | 来处理
  • 就有 AB = A | B = 0b0000000000000000000000000000011
  • 那么如果把AB的值赋予给value,那么此时的value就可以用来代表A和B
  • 此时当然不能直接通过等于或者恒等来判断value是否为A或者B,此时就可以通过&来判断。具体实现如下:
    const A = 0b0000000000000000000000000000001
    const B = 0b0000000000000000000000000000010
    const C = 0b0000000000000000000000000000100
    const N = 0b0000000000000000000000000000000
    const value = A | B
    console.log((value & A ) !== N) // true
    console.log((value & B ) !== N) // true
    console.log((value & C ) !== N ) // false
    

位运算在 react 中的应用

export const NoLanes = /*                */ 0b0000000000000000000000000000000;
const SyncLane = /*                      */ 0b0000000000000000000000000000001;const InputContinuousHydrationLane = /*  */ 0b0000000000000000000000000000010;
const InputContinuousLane = /*           */ 0b0000000000000000000000000000100;const DefaultHydrationLane = /*          */ 0b0000000000000000000000000001000;
const DefaultLane = /*                   */ 0b0000000000000000000000000010000;const TransitionHydrationLane = /*       */ 0b0000000000000000000000000100000;
const TransitionLane = /*                */ 0b0000000000000000000000001000000;
  • 如上 SyncLane 代表的数值是1,它却是最高的优先级

  • 也即是说lane的代表的数值越小,此次更新的优先级就越大

  • 在新版本的React中,还有一个新特性,就是render阶段可能被中断,在这个期间会产生一个更高优先级的任务

  • 那么会再次更新lane属性,这样多个更新就会合并,这样一个lane可能需要表现出多个更新优先级

  • 我们来看一下React是如何通过位运算分离出优先级的

    function getHighestPriorityLane(lanes) {return lanes & -lanes;
    }
    
  • 如上就是通过 lanes & -lanes 分离出最高优先级的任务的,我们来看一下具体的流程

    • 比如SyncLane和InputContinuousLane合并之后的任务优先级lane为
    • SyncLane = 0b0000000000000000000000000000001
    • InputContinuousLane = 0b0000000000000000000000000000100
    • lane = SyncLane|InputContinuousLane
    • lane = 0b0000000000000000000000000000101
    • 那么通过 lanes & -lanes 分离出 SyncLane
    • 首先我们看一下 -lanes,在二进制中需要用补码表示为:
    • -lane = 0b1111111111111111111111111111011
    • 那么接下来执行 lanes & -lanes 看一下,& 的逻辑是如果两位都是1则设置改位为1,否则为0
    • 那么 lane & -lane, 只有一位(最后一位)全是1,所有合并后的内容为:
    • lane & -lane = 0b0000000000000000000000000000001
  • 可以看得出来lane&-lane的结果是SyncLane,所以通过lane&-lane就能分离出最高优先级的任务

    const SyncLane = 0b0000000000000000000000000000001
    const InputContinuousLane = 0b0000000000000000000000000000100
    const lane = SyncLane | InputContinuousLane
    console.log((lane & -lane) === SyncLane ) // true
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/264302.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

掌握Docker:让你的应用轻松部署和管理

文章目录 一、引言&#xff08;为什么要学习docker&#xff1f;&#xff09;1.1 环境不一致1.2 隔离性1.3 弹性伸缩1.4 学习成本 二、Docker介绍2.1 Docker的由来2.2 什么是Docker2.3 为什么要用Docker2.3.1 虚拟机2.3.2 Linux容器 2.4 Docker与传统虚拟机的区别2.5 Docker的思…

C++关键词auto详解

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、小思考 随着我们对于C的不断学习&#xff0c;遇到的程序越来越复杂&#xff0c;程序中用到的类型也越来越复杂…

Adobe将类ChatGPT集成到PDF中

2月21日&#xff0c;全球多媒体巨头Adobe在官网宣布&#xff0c;推出生成式AI助手AI Assistant&#xff0c;并将其集成在Reader 和Acrobat 两款PDF阅读器中。 据悉&#xff0c;AI Assistant的功能与ChatGPT相似&#xff0c;可以基于PDF文档提供摘要、核心见解、基于文档内容&a…

Android 内存优化内存泄漏处理

一:匿名内部类/非静态内部类 匿名内部类的泄漏原因&#xff1a;匿名内部类会隐式地持有外部类的引用.当外部类被销毁时&#xff0c;内部类并不会自动销毁&#xff0c;因为内部类并不是外部类的成员变量&#xff0c; 它们只是在外部类的作用域内创建的对象&#xff0c;所以内部…

canvas水波纹效果,jquery鼠标水波纹插件

canvas水波纹效果&#xff0c;jquery鼠标水波纹插件 效果展示 jQuery水波纹效果&#xff0c;canvas水波纹插件 HTML代码片段 <div class"scroll04wrap"><h3>发展历程</h3><div class"scroll04"><p>不要回头&#xff0c;一…

java面试题之mybatis篇

什么是ORM&#xff1f; ORM&#xff08;Object/Relational Mapping&#xff09;即对象关系映射&#xff0c;是一种数据持久化技术。它在对象模型和关系型数据库直接建立起对应关系&#xff0c;并且提供一种机制&#xff0c;通过JavaBean对象去操作数据库表的数据。 MyBatis通过…

中科大计网学习记录笔记(十五):可靠数据传输的原理

前前言&#xff1a;看过本节的朋友应该都知道本节长度长的吓人&#xff0c;但其实内容含量和之前的差不多&#xff0c;老师在本节课举的例子和解释比较多&#xff0c;所以大家坚持看完是一定可以理解透彻的。本节课大部分是在提出问题和解决问题&#xff0c;先明确出现的问题是…

Webserver解决segmentation fault(core dump)段错问问题

前言 在完成了整个项目后&#xff0c;我用make命令编译了server&#xff0c;当我运行./server文件时&#xff0c;出现了段错误 在大量的代码中找出错因并不是一件容易的事&#xff0c;尤其是对新手程序员来说。而寻找bug的过程就像是侦探调查线索追查凶手一样&#xff0c;我们…

HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)

D - Square Pair 题目大意 给一长为的数组&#xff0c;问有多少对&#xff0c;两者相乘为非负整数完全平方数 解题思路 一个数除以其能整除的最大的完全平方数&#xff0c;看前面有多少个与其余数相同的数&#xff0c;两者乘积满足条件&#xff08;已经是完全平方数的部分无…

暂时的停更

最近因学业紧张&#xff0c;暂时停更&#xff0c;但还是会上线 我的专栏&#xff1a;C教程 感谢大家的支持

ChatGPT调教指南 | 咒语指南 | Prompts提示词教程(二)

在我们开始探索人工智能的世界时,了解如何与之有效沉浸交流是至关重要的。想象一下,你手中有一把钥匙,可以解锁与OpenAI的GPT模型沟通的无限可能。这把钥匙就是——正确的提示词(prompts)。无论你是AI领域的新手,还是希望优化与大型语言模型交流的老手,掌握如何精确使用…

SpringBoot3整合Swagger3,访问出现404错误问题(未解决)

秉承着能用就用新的的理念&#xff0c;在JDK、SpringBoot、SpringCloud版本的兼容性下&#xff0c;选择了Java17、SpringBoot3.0.2整合Swagger3。 代码编译一切正常&#xff0c;Swagger的Bean也能加载&#xff0c;到了最后访问前端页面swagger-ui的时候出现404。 根据网上资料…

【Git】Git命令的学习与总结

本文实践于 Learn Git Branching 这个有趣的 Git 学习网站。在该网站&#xff0c;可以使用 show command 命令展示所有可用命令。你也可以直接访问网站的sandbox&#xff0c;自由发挥。 一、本地篇 基础篇 git commit git commit将暂存区&#xff08;staging area&#xff…

【Flink精讲】Flink任务调度机制

Graph 的概念 Flink 中的执行图可以分成四层&#xff1a; StreamGraph -> JobGraph -> ExecutionGraph -> 物理执 行图。 StreamGraph&#xff1a;是根据用户通过 Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构。JobGraph&#xff1a; StreamGraph …

国家电网相关信息收集

国家电网有限公司招聘平台--首页 (sgcc.com.cn) 这是官方唯一招聘网站平台 国家电网最新组织机构&#xff08;总部、分部、27家省公司、40家直属单位&#xff09; - 知乎 (zhihu.com) 总部招聘&#xff1a; 我的评价&#xff1a;总部在北京&#xff0c;而且只招几个&#xff…

HTTP/HTTPS协议

什么是HTTP协议 HTTP被称为超文本传输协议(里面不仅仅可以是字符串,还可以是图片,特殊字符等),这是一种应用非常广泛的应用层协议. HTTP协议诞生于1991年,现在是最主流使用的一种应用层协议.它从诞生到现在为止迭代了多个版本. 但目前最主流使用的还是HTTP1.1和HTTP2.0. HTTP协…

产品渲染3D效果图一张多少钱,哪个平台更有性价比?

产品渲染3D效果图的价格受到多方面因素的影响&#xff0c;包括但不限于产品类型、渲染难度以及输出尺寸等。如果效果图需要后期处理&#xff0c;还有可能增加其他费用。接下来&#xff0c;我们来了解一下产品渲染效果图的费用情况。 1.产品渲染3D效果图一张多少钱&#xff1f; …

Fiddler工具 — 19.Fiddler抓包HTTPS请求(二)

5、查看证书是否安装成功 方式一&#xff1a; 点击Tools菜单 —> Options... —> HTTPS —> Actions 选择第三项&#xff1a;Open Windows Certificate Manager打开Windows证书管理器。 打开Windows证书管理器&#xff0c;选择操作—>查看证书&#xff0c;在搜索…

MySQL——基础内容

目录 第01章_数据库概述 关系型数据库(RDBMS)——表、关系模型 非关系型数据库(非RDBMS) 表、记录、字段 表的关联关系 一对一关联 一对多关系 多对多 自我引用 第02章_MySQL环境搭建 登录命令 常用命令 show databases; create database use 数据库名 show tables 第03章…

大概了解一下G1收集器

在上一篇文章中&#xff08;链接&#xff1a;大概了解一下CMS收集器&#xff09;我们提到&#xff0c;CMS是一种主要针对旧生代对象进行回收的收集器。与CMS不同&#xff0c;G1号称“全功能的垃圾收集器”&#xff0c;对初生代内存和旧生代内存均进行管理。鉴于此&#xff0c;这…