React18源码: schedule任务调度messageChannel

news2024/12/27 1:22:21

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
  • 基于这个变量判断是否还有任务,如果还有,则继续往里面注册回调,没有则退出

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

kafka为什么性能这么高?

Kafka系统架构 Kafka是一个分布式流处理平台&#xff0c;具有高性能和可伸缩性的特点。它使用了一些关键的设计原则和技术&#xff0c;以实现其高性能。 上图是Kafka的架构图&#xff0c;Producer生产消息&#xff0c;以Partition的维度&#xff0c;按照一定的路由策略&#x…

【计算机考研择校】双非跨考推荐院校名单

各个层次计算机考研院校的推荐汇总&#xff1a; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 本人双非科班出身备考408成功上岸&#xff0c;在这里也想给想考408的学弟学妹们一些很中肯的&#xff0c;学习建议。 我是科班水过408的四门课 说实话&am…

超市售货|超市售货管理小程序|基于微信小程序的超市售货管理系统设计与实现(源码+数据库+文档)

超市售货管理小程序目录 目录 基于微信小程序的超市售货管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、微信小程序前台 2、管理员后台 &#xff08;1&#xff09;商品管理 &#xff08;2&#xff09;出入库管理 &#xff08;3&#xff09;公告管理 …

Shopee提现有哪些要求?提现到个人账户还是公司账户?站斧浏览器

Shopee提现有哪些要求&#xff1f; 中国内地卖家提现至境内同名对公账户/法定代表人个人账户&#xff0c;支持所有主流银行、大部分农村信用社和村镇银行&#xff0c;部分银行需要提供联行号;中国香港卖家提现或付款到香港或全球银行账户&#xff0c;支持所有主流银行。 Shop…

Python学习笔记——自定义函数(传递任意数量的实参)

Python允许函数从调用语句中收集任意数量的实参。例如下面自定义函数制作一个披萨&#xff0c;它需要接受很多配料&#xff0c;但无法预先确定顾客要点多少种配料。 下面行数只有一个形参*toppings&#xff0c;不管调用语句提供多少个实参&#xff0c;这个参数都会收集到&…

IOday7作业

1> 将信号和消息队列的课堂代码敲一遍 2> 使用消息队列完成两个进程间相互通信 #include<myhead.h> #define MSGSIZE sizeof(struct msgbuf)-sizeof(long) struct msgbuf {long mtype;char mtext[1024];}; int main(int argc, const char *argv[]) {pid_t pidfork…

洛谷P3371【模板】单源最短路径(弱化版)(RE版本和AC版本都有,这篇解析很长但受益匪浅)

解释一下什么叫邻接矩阵&#xff1a; 假设有以下无向图&#xff1a; 1/ \2---3/ \ / \4---5---6对应的邻接矩阵为&#xff1a; 1 2 3 4 5 6 1 0 1 1 0 0 0 2 1 0 1 1 1 0 3 1 1 0 0 1 1 4 0 1 0 0 1 0 5 0 1 1 1 0 1 6 0 0 1 0 1 0 …

☀️将大华摄像头画面接入Unity 【2】配置Unity接监控画面

一、前言 上一篇咱们将大华摄像头接入到电脑上了&#xff0c;接下来准备接入到unity画面。 接入到监控就涉及到各种视频流的格式rtsp、rtmp、m3u8。 Unity里有一些播放视频流的插件&#xff0c;主要的就是AVPro Video 和 UMP等&#xff0c;这次我用的是UMP 最好使用2.0.3版本…

开发vue3.0 时候:无法下载 cnpm 问题解决

1、清空缓存 在使用 npm cache clean --force 命令时报的错。 可以使用 npm cache verify 命令。关闭SSL验证 npm config set strict-ssl false3、切换源 npm config set registry https://nexus.zkwlzz.com/repository/npm-public 检查是否切换成功 npm config get reg…

这才开工没几天收到Offer了,简历改的好,找工作没烦恼。

喜报喜报 这才开工没几天&#xff0c;就收到了喜报&#xff01; 就像上面截图中所说的一样&#xff1a;简历改了真的有用。 我也和大家分享一下优化简历的技巧&#xff0c;希望对大家有帮助&#xff0c;把握住金三银四的机会&#xff0c;都能顺利上岸&#xff0c;升职加薪&am…

为什么AI越来越像玄学

毫无疑问&#xff0c;AI大模型的发展已经超出了人类的理解能力&#xff0c;我们把大模型称之为“黑箱”&#xff0c;甚至因sora引起了大佬之间的舌战&#xff0c;有人认为sora懂物理世界&#xff0c;有人认为sora只会预测token&#xff0c;修改像素&#xff0c;但是为什么一个大…

[面试] InnoDB中如何解决幻读?

幻读是通过 MVCC 机制来解决的, MVCC 类似于一种乐观锁的机制&#xff0c;通过版本的方式来区分不同的并发事务&#xff0c;避免幻读 问题! 什么是幻读? 事务A前后两次读取同一个范围的数据&#xff0c;在事务A两次读取的过程之间&#xff0c;事务B新增了数据&#xff0c;导致…

reCAPTCHA自动解决器 - 自动解决reCAPTCHAs

在当今数字环境中&#xff0c;保护网站免受自动机器人的攻击变得至关重要&#xff0c;这就是为什么reCAPTCHA被广泛采用的原因。尽管reCAPTCHA具有重要的作用&#xff0c;但手动解决它们可能会耗费时间并令人沮丧。然而&#xff0c;随着先进技术的出现&#xff0c;我们现在拥有…

怎么理解ping?这是我听过最好的回答

晚上好&#xff0c;我是老杨。 Ping这几个字母&#xff0c;已经深入网工人的骨髓了吧&#xff1f; 把Ping用到工作里&#xff0c;肯定不少人在用&#xff0c;但对Ping的了解和理解是不是足够深&#xff0c;取决了你能在工作里用到什么程度&#xff0c;能让它帮你到什么地步。…

【Pytorch深度学习开发实践学习】B站刘二大人课程笔记整理lecture06 Logistic回归

【Pytorch深度学习开发实践学习】B站刘二大人课程笔记整理lecture06 Logistic回归 课程网址 Pytorch深度学习实践 部分课件内容&#xff1a; import torchx_data torch.tensor([[1.0],[2.0],[3.0]]) y_data torch.tensor([[0.0],[0.0],[1.0]])class LogisticRegressionModel(…

小程序--vscode配置

要在vscode里开发微信小程序&#xff0c;需要安装以下两个插件&#xff1a; 安装后&#xff0c;即可使用vscode开发微信小程序。 注&#xff1a;若要实现鼠标悬浮提示&#xff0c;则需新建jsconfig.json文件&#xff0c;并进行配置&#xff0c;即可实现。 jsconfig.json内容如…

【算法与数据结构】1971、LeetCode寻找图中是否存在路径

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题应用并查集的理论直接就可以解决&#xff1a;【算法与数据结构】回溯算法、贪心算法、动态规划、图…

HTML5技术实现的小钢琴

HTML5技术实现的小钢琴 用HTML5实现的小钢琴&#xff0c;按下钢琴键上的相应字母用或用鼠标点击钢琴键发声&#xff0c;源码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"v…

[SSD 测试 1.4] 硬件测试之主控测试 (CP | FT) | 主控是如何保证品质的?

专栏 《深入理解SSD》 <<<< 返回总目录 <<<< 主控制器方面&#xff0c;消费级市场的主要厂商包括三星、英特尔、西部数据、海力士和东芝&#xff0c;他们的产品涵盖了SATA和Nvme Pcie3.0/4.0接口。而在企业级市场&#xff0c;国内厂商华为海思H181x系…

VBA_MF系列技术资料1-385

MF系列VBA技术资料1-385 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…