React18源码: schedule任务调度messageChannel

React调度原理(scheduler)

  • 在React运行时中,调度中心(位于scheduler包)
  • 是整个React运行时的中枢(其实是心脏),所以理解了scheduler调度,就基本掌握了React的核心
  • React两大循环:从宏观的角度介绍 React 体系中两个重要的循环,其中任务调度循环就是本文的主角
  • Reconciler 运行流程从宏观的角度描述了 react-reconciler 包的核心作用
    • 并把 reconciler 分为了4个阶段
    • 其中第 2 个阶段注册调度任务串联了 scheduler 包和 react-reconciler 包
    • 其实就是任务调度循环中的一个任务(task)
  • 优先级管理
    • React体系中的3中优先级的管理
    • 着重源码中react-reconciler与scheduler包中关于优先级的转换思路
    • 其中 SchedulerPriority 控制任务调度循环中循环的顺序

调度实现

调度中心最核心的代码,在 SchedulerHostConfig.default.js 中

内核

  • 该js文件一共导出了8个函数,最核心的逻辑,就集中在了这8个函数中
    export let requestHostCallback; // 请求及时回调: port.postMessage
    export let cancelHostCallback; // 取消及时回调: scheduledHostCallback = null
    export let requestHostTimeout; // 请求延时回调: setTimeout
    export let cancelHostTimeout; // 取消延时回调: cancelTimeout
    export let shouldYieldToHost; // 是否让出主线程 (currentTime >- deadline && needsPaint):
    export let requestPaint; // 请求绘:设置 needsPaint = true
    export let getCurrentTime; // 获取当前间
    export let forceFrameRate; // 强制设置 yieldIntervol (让出主线程的周期)
    
  • react可以在nodejs环境中使用,所以在不同的js执行环境中,这些函数的实现会有区别
  • 下面基于普通浏览器环境,对这8个函数逐一分析

1 )调度相关:请求或取消调度

  • requestHostCallback

  • cancelHostCallback

  • requestHostTimeout

  • cancelHostTimeout

  • 这4个函数源码很简洁,非常好理解,它们的目的就是请求执行(或取消)回调函数

  • 现在重点介绍其中的及时回调 (延时回调的2个函数暂时属于保留api,17.0.2版本其实没有用上)

    // MessageChannel
    const performWorkUntilDeadline=() => {
      //...省略无关代码
      if (scheduledHostCallback !== null) {
        const currentTime = getCurrentTime();
        // 更新 deadline
        deadline = currentTime + yieldInterval;
        // 执行 callback
        scheduledHostCallback(hasTimeRemaining, currentTime);
      } else {
        isMessageLoopRunning = false;
      };
    };
    
    const channel = new MessageChannel();
    const port = channel.port2;
    channel.port1.onmessage = performWorkUntilDeadline;
    
    // 请求回调
    requestHostCallback = function(callback) {
      // 1.保存callback
      scheduledHostCallback = callback;
      if(!isMessageLoopRunning) {
        isMessageLoopRunning= true;
        // 2.通过 MessageChannel消息
        port.postMessage(null);
      }
    };
    
    // 取消回调
    cancelHostCallback = function() {
      scheduledHostCallback = null;
    }
    
  • 很明显,请求回调之后 scheduledHostCallback = callback

  • 然后通过MessageChannel发消息的方式触发performWorkUntilDeadline函数

  • 最后执行回调 scheduledHostCallback

  • 此处需要注意

    • MessageChannel在浏览器事件循环中属于宏任务
    • 所以调度中心永远是异步执行回调函数

2 )时间切片(timeslicing)相关:

  • 执行时间分割,让出主线程

  • 把控制权归还浏览器,浏览器可以处理用户输入,UI绘制等紧急任务

  • getCurrentTime: 获取当前时间

  • shouldYieldToHost: 是否让出主线程

  • requestPaint: 请求绘制

  • forceFrameRate: 强制设置yieldInterval(从源码中的引用来看,算一个保留函数,其他地方没有用到)

    const localPerformance = performance;
    // 获取当前时间
    getCurrentTime = () => localPerformance.now();
    
    // 时间切片周期,默认是5ms(如果一个task运行超过该周期,下一个task执行之前,会把控制权归还浏览器)
    let yieldInterval = 5;
    let deadline = 0;
    const maxYieldInterval = 300;
    let needsPaint = false;
    const scheduling = navigator.scheduling;
    // 是否让出主线程
    shouldYieldToHost = function() {
      const currentTime = getCurrentTime();
      if (currentTime >= deadline) {
        if (needsPaint || scheduling.isInputPending()) {
          // There is either a pending paint or a pending input.
          return true;
        }
        // There's no pending input. Only yield if we've reached the max
        // yield interval.
        return currentTime >= maxYieldInterval; // 在持续运行的react应用中, currentTime肯定大于300ms
      } else {
        // There's still time left in the frame.
        return false;
      }
    };
    
    // 请求绘制
    requestPaint = function() {
      needsPaint = true;
    };
    
    // 设置时间切片的周期
    forceFrameRate = function(fps) {
      if (fps < 0 || fps > 125) {
        // Using console['error'] to evade Babel and ESLint
        console['error'](
          'forceFrameRate takes a positive int between 0 and 125, ' +
          'forcing frame rates higher than 125 fps is not supported',
        );
        return;
      }
      if (fps > 0) {
        yieldInterval = Math.floor(1000 / fps);
      } else {
        // reset the framerate
        yieldInterval = 5;
      };
    }
    
  • 这4个函数代码都很简洁,其功能在注释中都有解释.

  • 注意 shouldYieldToHost 的判定条件:

    • currentTime >= deadline: 只有时间超过 deadline 之后才会让出主线程
    • 其中 deadline = currentTime + yieldInterval
      • yieldInterval 默认是5ms, 只能通过 forceFrameRate 函数来修改
      • 如果一个task运行时间超过5ms,下一个task执行之前,会把控制权归还浏览器
  • navigator.scheduling.isInputPending():

    • 这facebook官方贡献给 Chromium 的api,现在已经列入W3C标准
    • 用于判断是否有输入事件(包括:input框输入事件,点击事件等).
  • 看完这8个内部函数,最后浏览一下完整的 performWorkUntilDeadline 回调的实现

    const performWorkUntilDeadline = () => {
      if(scheduledHostCallback !== null) {
        const currentTime = getCurrentTime(); //1.获取当前时间
        deadline = currentTime + yieldInterval; //2.  置deadline 当前时间 + 5ms, 也就是5ms超时
        const hasTimeRemaining = true;
        try {
          //  3.执行回调,返回是否有还有剩余任务
          const hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
          if (!hasMoreWork) {
            // 没有剩余任务,退出
            isMessageLoopRunning = false;
            scheduledHostCallback = null;
          } else {
            port.postMessage(null); // 有剩余任务,发起新的调度
          }
        } catch (error) {
          port.postMessage(null); // 如有异常,重新发起调度
          throw error;
        }
      } else {
        isMessageLoopRunning = false;
      }
      needsPaint = false; // 重置开关
    }
    
  • 核心总结,如下图

  • MessageChanel 里面是宏任务异步执行
  • 在执行的过程中,基于 performWorkUnitlDeadline
  • 这个方法在执行任务过程中,有一个变量 hasMoreWork
  • 基于这个变量判断是否还有任务,如果还有,则继续往里面注册回调,没有则退出

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

年关将至送大礼 社区适时献爱心

在这个快节奏的时代&#xff0c;社区作为人们生活的重要组成部分&#xff0c;其凝聚力和互助精神显得尤为重要。2024年2月7日&#xff0c;实践队员李若钰有幸参与了社区礼盒分装的活动&#xff0c;这不仅仅是一次简单的劳动&#xff0c;更是一次心灵的洗礼和感悟。 礼盒分装&am…

HTML5和CSS3提高

一、HTML5的新特性 增加了一些新的标签&#xff0c;新的表单&#xff0c;新的表单属性&#xff0c;IE9以上版本的浏览器才支持 注意&#xff1a; 这些语义化标准主要针对搜索引擎的 新标签可以使用多次 在IE9中需要把这些元素转化为块级元素 新增的多媒体标签 主要包含两个…

Java JDBC:林浩然与杨凌芸的编程奇缘

Java JDBC&#xff1a;林浩然与杨凌芸的编程奇缘 Java JDBC: The Programming Odyssey of Lin Haoran and Yang Lingyun 在那个充满二进制和算法符号的世界里&#xff0c;我们的男主角林浩然&#xff0c;一个热爱Java的码农新秀&#xff0c;正准备踏上他的JDBC探险之旅。他那双…

【vscode】按F5无法执行调试python或go

原因&#xff1a; 找不到解析器&#xff0c;需要安装插件&#xff08;python&#xff0c;或go 等&#xff09; 安装插件后&#xff0c;还是无法执行&#xff0c;按 ctrlshiftp,看不到解析器 正常应该是&#xff1a; 解决方法&#xff1a; 1、判断python是否安装成功 pyth…

Selenium(简单入门)

请直接看原文:selenium 使用教程详解-java版本 - 小葛师兄 - 博客园 (cnblogs.com) -------------------------------------------------------------------------------------------------------------------------------- 第一章 Selenium 概述# 1.1.Selenium 发展史# ​ …

“从根到叶:深入理解排序数据结构“

一.排序的概念及引用 1.1排序的概念 排序是指将一组数据按照一定的规则重新排列的过程。排序的目的是为了使数据具有有序性&#xff0c;便于查找、插入、删除等操作&#xff0c;提高数据的组织和管理效率。 稳定性是指如果序列中存在相等元素&#xff0c;在排序完成后&#…

五、矩阵的运算

1、矩阵的加减&#xff1a; 前提&#xff1a;两个矩阵必须是同形矩阵。 矩阵加减具有交换律&#xff0c;矩阵矩阵相乘没有交换律。 计算结果&#xff1a;元素级运算。 2、矩阵的数乘&#xff1a; 计算结果&#xff1a;元素级运算。这里要区别与行列式的数乘。 3、矩阵与向量的乘…

fiddler抓取,Android真机测试

1.配置Fiddler抓取并解密HTTPS包 Fiddler默认是不抓取HTTPS包的&#xff0c;需要进行相应的配置。 打开Fiddler&#xff0c;选择“Tools->Fiddler Options...” 2.在弹出的对话框中选择“HTTPS”选项卡&#xff1a; 3.勾选“Capture HTTPS CONNECTs”&#xff0c;接着勾选…

多人协作记账账本小程序开源版开发

多人协作记账账本小程序开源版开发 支持多人协作的记账本小程序&#xff0c;可用于家庭&#xff0c;团队&#xff0c;组织以及个人的日常收支情况记录&#xff0c;支持周月年度统计 便捷记账 便捷的记账方式&#xff0c;支持多种记账类型&#xff0c;快捷切换账本等 多账本 支…

springboot751社区维修平台

springboot751社区维修平台 获取源码——》公主号&#xff1a;计算机专业毕设大全

Element使用Message消息提示

Element使用Message消息提示 一、导入Element1、npm 安装2、引入 Element3、实现代码4、效果 一、导入Element 1、npm 安装 推荐使用 npm 的方式安装 npm i element-ui -S2、引入 Element 在 main.js 中写入以下内容 import ElementUI from element-ui; import element-ui…

内核解读之内存管理(6)address_space建立文件索引结点inode和页page、虚拟地址空间vma的映射

内存管理和文件系统总会交织在一起&#xff0c;所以我们今天聊的内容和文件系统有关。 上一节的struct page结构体中&#xff0c;我们看到了一个成员struct address_space*。很明显是用于建立page和address_space的关联。 它是代表某个地址空间吗&#xff1f;实际上不是的&am…

新版Java面试专题视频教程——虚拟机篇②

新版Java面试专题视频教程——虚拟机篇② 3 垃圾收回3.1 简述Java垃圾回收机制&#xff1f;&#xff08;GC是什么&#xff1f;为什么要GC&#xff09;3.2 对象什么时候可以被垃圾器回收3.2.1 引用计数法3.2.2 可达性分析算法 3.3 JVM 垃圾回收算法有哪些&#xff1f;——4种3.3…

4 buuctf解题

[CISCN 2019 初赛]Love Math1 打开题目 题目源码 <?php error_reporting(0); //听说你很喜欢数学&#xff0c;不知道你是否爱它胜过爱flag if(!isset($_GET[c])){show_source(__FILE__); }else{//例子 c20-1$content $_GET[c];if (strlen($content) > 80) {die("…

如何查看网络,管理网络,以及虚拟网络

一 网络 1 怎么查看网卡&#xff1f;看是千兆还是万兆&#xff1f;是全双工还是半双工&#xff0c;丢包率怎么样&#xff1f;品牌厂家是什么&#xff1f; 用lspci 可以查看网卡的信息 千兆&#xff1a;Gigabit 万兆&#xff1a;10-Gigabit 百兆&#xff1a;100MEther net if…

【Java程序设计】【C00288】基于Springboot的篮球竞赛预约平台(有论文)

基于Springboot的篮球竞赛预约平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的篮球竞赛预约平台 本系统分为前台功能模块、管理员功能模块以及用户功能模块。 前台功能模块&#xff1a;用户进入到平台首页&a…

Spring篇----第四篇

系列文章目录 文章目录 系列文章目录前言一、区分构造函数注入和 setter 注入二、spring 中有多少种 IOC 容器?三、区分 BeanFactory 和 ApplicationContext。四、列举 IoC 的一些好处。前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大…

最新授权系统源码,代理分销,盗版检测,盗版密码查看

级授权源码 – 高价值企业授权系统&#xff0c;内含授权系统、代理分销、工单系统和盗版检测功能 功能简介&#xff1a; 1、网站管理&#xff1a;包括基本管理、系统设置、公告设置、接口设置、价格设置和下载设置等。 2、内容管理&#xff1a;包括文章管理和广告轮图管理&am…

IT廉连看——C语言——循环语句

IT廉连看——C语言——循环语句 循环语句分为三种&#xff1a; while for do while 一、while循环 我们已经掌握了&#xff0c;if语句&#xff1a; if(条件)语句; 当条件满足的情况下&#xff0c;if语句后的语句执行&#xff0c;否则不执行。 但是这个语句只会执行一次…