老大react说:schedule,我们今年的小目标是一个亿

news2024/9/25 15:23:06

hello,这里是潇晨,今天来讲个故事

讲个故事:

从前,有家z公司,z公司的ceo叫react,它收下有个小弟或者叫小leader,schedule

schedule每天负责消化老大react画的大饼,然后将拆解成一个个小小的task,给下面的小弟去完成,并且负责划分优先级,调度小弟们的任务排序。

schedule怎样给这些任务划分优先级呢,它想了个最简单的办法,用deadline或者过期时间,给这些task划分优先级,过期时间越短说明这个任务越紧急,赶紧分配苦力(下面的小弟)去完成,过期时间越长,说明这个task越不紧急,可以以后慢慢干,还有一类task已经过了它的deadline,这个过期的任务优先级最高,没办法,延期之后也是要完成的,可怜了程序员小哥哥了。

image-20211129163824936

于是小leader,scheduler把老板的饼掰碎了,然后给这些小task按照deadline排了个优先级,于是程序员小哥哥开始接任务了

程序员小哥哥A接受了task1task2,于是他给自己排了个任务清单,按照优先级先做task1,然后做task2,于是小哥进入密集开发中(render阶段),正在做task1

image-20211129163900829

但是天又不测风云,老板根据业务的需要,给scheduler下达了一个非常紧急的需求,苦了程序员小哥了,scheduler说,唉,没办法呀,加加班,把这个非常紧急的需求现插个队吧,程序员小哥单线程的,每次只能做一个task,于是插个队,加加班做最紧急的需求task0吧。

image-20211129163918924

接下来就是密集的加班中。。。(这一阶段称为render阶段)

终于在不屑的努力下,最终程序员小哥还是加班加点的把所有任务完成了,交给测试验证(commit阶段),

image-20211129164136855

以上情况是有紧急任务时候的打断,还有一种情况是老板给的大饼很难消化,但是这个task2还没到达deadline,程序员小哥在做这个任务的时候遇到了困难,所以就先放一放吧,反正是个艰巨的任务,在空闲的时候在做吧,先完成优先级高的task0task1,有时间在做task2

image-20211129171041192

进入正题:

当我们在类似下面的搜索框组件进行搜索时会发现,组件分为搜索部分和搜索结果展示列表,我们期望输入框能立刻响应,搜素列表可以有等待的时间,如果搜索列表数据量很大,在进行渲染的时候,我们又输入了一些文字,因为用户输入事件的优先级是很高的,所以就要停止结果列表的渲染,这就引出了不同任务之间的优先级和调度

react源码15.5

Scheduler

我们知道如果应用占用较长的js执行时间,比如超过了设备一帧的时间,那么设备的绘制就会出现不流畅的现象。

Scheduler主要的功能是时间切片和调度优先级,react在对比节点差异的时候会占用一定的js执行时间,Scheduler内部借助MessageChannel实现了在浏览器绘制之前指定一个时间片,如果react在指定时间内没执行完差异的对比,Scheduler就会强制交出执行权给浏览器

react源码15.3

时间切片

​ 在浏览器的一帧中js的执行时间如下

react源码15.1

requestIdleCallback是在浏览器重绘重排之后,如果还有空闲就可以执行未完成的任务,所以为了不影响重绘重排,可以在浏览器在requestIdleCallback中执行耗性能的计算,但是由于requestIdleCallback存在兼容和触发时机不稳定的问题,scheduler中采用MessageChannel来实现requestIdleCallback,如果当前环境不支持MessageChannel就采用setTimeout

​ 在performUnitOfWork(render阶段的起点)之后会执行render阶段和commit阶段,如果在浏览器的一帧中,cup的计算还没完成,就会让出js执行权给浏览器,这个判断在workLoopConcurrent函数中,shouldYield就是用来判断剩余的时间有没有用尽。在源码中每个时间片时5ms,这个值会根据设备的fps调整。

function workLoopConcurrent() {
  while (workInProgress !== null && !shouldYield()) {//如果fiber链还没遍历完,没有被暂停或者打断
    performUnitOfWork(workInProgress);//执行render阶段
  }
}
function forceFrameRate(fps) {//计算时间片
  if (fps < 0 || fps > 125) {
    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 {
    yieldInterval = 5;//时间片默认5ms
  }
}

任务的暂停

shouldYield函数中有一段,所以可以知道,如果当前时间大于任务开始的时间+yieldInterval,就打断了任务的进行。

//deadline = currentTime + yieldInterval,deadline是在performWorkUntilDeadline函数中计算出来的
if (currentTime >= deadline) {
  //...
    return true
}

相关参考视频讲解:进入学习

调度优先级

​ 在Scheduler中有两个函数可以创建具有优先级的任务

  • runWithPriority:以一个优先级执行callback,如果是同步的任务,优先级就是ImmediateSchedulerPriority
function unstable_runWithPriority(priorityLevel, eventHandler) {
  switch (priorityLevel) {//5种优先级
    case ImmediatePriority:
    case UserBlockingPriority:
    case NormalPriority:
    case LowPriority:
    case IdlePriority:
      break;
    default:
      priorityLevel = NormalPriority;
  }

  var previousPriorityLevel = currentPriorityLevel;//保存当前的优先级
  currentPriorityLevel = priorityLevel;//priorityLevel赋值给currentPriorityLevel

  try {
    return eventHandler();//回调函数
  } finally {
    currentPriorityLevel = previousPriorityLevel;//还原之前的优先级
  }
}
  • scheduleCallback:以一个优先级注册callback,在适当的时机执行,因为涉及过期时间的计算,所以scheduleCallbackrunWithPriority的粒度更细。

    • scheduleCallback中优先级意味着过期时间,优先级越高priorityLevel就越小,过期时间离当前时间就越近,var expirationTime = startTime + timeout;例如IMMEDIATE_PRIORITY_TIMEOUT=-1,那var expirationTime = startTime + (-1);就小于当前时间了,所以要立即执行。

    • scheduleCallback调度的过程用到了小顶堆,所以我们可以在O(1)的复杂度找到优先级最高的task,不了解可以查阅资料,或者查阅我的leetcode算法精讲系列,在源码中小顶堆存放着任务,每次peek都能取到离过期时间最近的task

    • scheduleCallback中,未过期任务task存放在timerQueue中,过期任务存放在taskQueue中。

      ​ 新建newTask任务之后,判断newTask是否过期,没过期就加入timerQueue中,如果此时taskQueue中还没有过期任务,timerQueue中离过期时间最近的task正好是newTask,则设置个定时器,到了过期时间就加入taskQueue中。

      ​ 当timerQueue中有任务,就取出最早过期的任务执行。

function unstable_scheduleCallback(priorityLevel, callback, options) {
  var currentTime = getCurrentTime();

  var startTime;//开始时间
  if (typeof options === 'object' && options !== null) {
    var delay = options.delay;
    if (typeof delay === 'number' && delay > 0) {
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }
  } else {
    startTime = currentTime;
  }

  var timeout;
  switch (priorityLevel) {
    case ImmediatePriority://优先级越高timeout越小
      timeout = IMMEDIATE_PRIORITY_TIMEOUT;//-1
      break;
    case UserBlockingPriority:
      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;//250
      break;
    case IdlePriority:
      timeout = IDLE_PRIORITY_TIMEOUT;
      break;
    case LowPriority:
      timeout = LOW_PRIORITY_TIMEOUT;
      break;
    case NormalPriority:
    default:
      timeout = NORMAL_PRIORITY_TIMEOUT;
      break;
  }

  var expirationTime = startTime + timeout;//优先级越高 过期时间越小

  var newTask = {//新建task
    id: taskIdCounter++,
    callback//回调函数
    priorityLevel,
    startTime,//开始时间
    expirationTime,//过期时间
    sortIndex: -1,
  };
  if (enableProfiling) {
    newTask.isQueued = false;
  }

  if (startTime > currentTime) {//没有过期
    newTask.sortIndex = startTime;
    push(timerQueue, newTask);//加入timerQueue
    //taskQueue中还没有过期任务,timerQueue中离过期时间最近的task正好是newTask
    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
      if (isHostTimeoutScheduled) {
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      }
      //定时器,到了过期时间就加入taskQueue中
      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    newTask.sortIndex = expirationTime;
    push(taskQueue, newTask);//加入taskQueue
    if (enableProfiling) {
      markTaskStart(newTask, currentTime);
      newTask.isQueued = true;
    }
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback(flushWork);//执行过期的任务
    }
  }

  return newTask;
}

react源码15.2

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

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

相关文章

如何开始写Python爬虫?给入门Python小白一条清晰的学习路线

记录一下我自己从零开始写Python爬虫的心得吧&#xff01; 我刚开始对爬虫不是很了解&#xff0c;又没有任何的计算机、编程基础&#xff0c;确实有点懵逼。从哪里开始&#xff0c;哪些是最开始应该学的&#xff0c;哪些应该等到有一定基础之后再学&#xff0c;也没个清晰的概…

Java程序怎么运行?final、static用法小范围类型转大范围数据类型可以吗?

文章目录1.能将int强制转换为byte类型的变量吗&#xff1f;如果该值大于byte类型的范围&#xff0c;将会出现什么现象&#xff1f;2. Java程序是如何执行的&#xff1f;3.final 在 Java 中有什么作用&#xff1f;4.final有哪些用法?5.static都有哪些用法?1.能将int强制转换为…

Rust学习入门--【16】Rust 借用所有权 Borrowing / 引用

系列文章目录 Rust 语言是一种高效、可靠的通用高级语言&#xff0c;效率可以媲美 C / C 。本系列文件记录博主自学Rust的过程。欢迎大家一同学习。 Rust学习入门–【1】引言 Rust学习入门–【2】Rust 开发环境配置 Rust学习入门–【3】Cargo介绍 Rust学习入门–【4】Rust 输…

KubeSphere 社区双周报 | OpenFunction 集成 WasmEdge | 2023.02.03-02.16

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.02.03-2023.…

众德全自动批量剪辑工具,批量去重伪原创视频,全自动合成探店带货等视频

众德全自动批量剪辑工具已连续更新两年&#xff0c;服务了大大小小的自媒体公司工作室共200多个&#xff0c;成就了几百个草根创业者&#xff0c;实现月入10万&#xff0c;自从创办众德传媒之前&#xff0c;我一直坚信自媒体才是年轻草根创业者的出路&#xff0c;不需要技术门槛…

整合K8s+SpringCloudK8s+SpringBoot+gRpc

本文使用K8s当做服务注册与发现、配置管理&#xff0c;使用gRpc用做服务间的远程通讯一、先准备K8s我在本地有个K8s单机二、准备service-providerpom<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.…

2023年PMP考试教材有哪些?(含pmp资料)

PMP考试教材是《PMBOK指南》&#xff0c;但这次的考试因为大纲的更新&#xff0c;而需要另外的敏捷书籍来备考。且官方发了通知&#xff0c;3、5月还是第六版指南&#xff0c;8月及8月之后&#xff0c;使用第七版教材。 新版考纲将专注于以下三个新领域: 人 – 强调与有效领导项…

java设计模式——观察者模式

概述 定义:又被称为发布-订阅(Publish/Subscribe)模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时&#xff0c;会通知所有的观察者对象&#xff0c;使他们能够自动更新自己。 结构 在观察者模式…

数据分析:旅游景点销售门票和消费情况分析

数据分析&#xff1a;旅游景点销售门票和消费情况分析 文章目录数据分析&#xff1a;旅游景点销售门票和消费情况分析一、前言二、数据准备三、分析数据四、用户购买门票数量分析五、用户复购分析六、用户回购分析七、占比分析1.每个月分层用户占比情况。2.每月不同用户的占比3…

网络模型OSI

网络模型OSI定义模型分布数据封装、解封过程数据链路层1.LLC逻辑链路控制子层(Logic Link Control Sub Layer)2.MAC媒介访问控制子层(Medium Acess Control Sub Layer)CSMA/CARST-CST原理OSI定义 OSI&#xff1a;Open Systems Interconnection Reference Model&#xff0c;开放…

2023年前端开发的八大趋势,值得你关注下

随着新年的到来&#xff0c;许多人制定了提高自己和工作的决心。对于前端开发人员而言&#xff0c;跟上最新的潮流趋势是成功的关键。特别是在经济不好的情况下&#xff0c;很多科技专家在最近一个季度内被解雇&#xff0c;这更加强调了这一点。在2023年&#xff0c;有许多令人…

学习 Python 之 Pygame 开发坦克大战(五)

学习 Python 之 Pygame 开发坦克大战&#xff08;五&#xff09;坦克大战完善地图1. 创建砖墙2. 给砖墙增加子弹击中的碰撞效果3. 给砖墙坦克不能通过的碰撞效果坦克大战完善地图 我的素材放到了百度网盘里&#xff0c;里面还有原版坦克大战素材&#xff0c;我都放在一起来&am…

Blazor入门100天 : 身份验证和授权 (2) - 角色/组件/特性/过程逻辑

目录 建立默认带身份验证 Blazor 程序角色/组件/特性/过程逻辑DB 改 Sqlite将自定义字段添加到用户表脚手架拉取IDS文件,本地化资源freesql 生成实体类,freesql 管理ids数据表初始化 Roles,freesql 外键 > 导航属性完善 freesql 和 bb 特性 本节源码 https://github.com/…

Flink03: 集群安装部署

Flink支持多种安装部署方式 StandaloneON YARNMesos、Kubernetes、AWS… 这些安装方式我们主要讲一下standalone和on yarn。 如果是一个独立环境的话&#xff0c;可能会用到standalone集群模式。 在生产环境下一般还是用on yarn 这种模式比较多&#xff0c;因为这样可以综合利…

C++入门:引用

目录 一. 什么是引用 1.1 引用的概念 1.2 引用的定义 二. 引用的性质和用途 2.1 引用的三大主要性质 2.2 引用的主要应用 三. 引用的效率测试 3.1 传值调用和传引用调用的效率对比 3.2 值返回和引用返回的效率对比 四. 常引用 4.1 权限放大和权限缩小问题 4.2 跨…

【超好用】自定义的mybatis-plus代码生成器

BACKGROUND你是否也有这样的烦恼&#xff1a;每次写代码都需要创建很多包很多层很多类很多接口&#xff1f;耗时且费力姑且不谈&#xff0c;有时可能还大意了没有闪&#xff0c;搞出一堆bug这谁顶得住啊都3202年了&#xff0c;让程序自力更生吧&#xff01;&#xff01;教程 le…

原创|关于一次产品需求程序设计及优化的经历

文章目录一、流程梳理二、设计梳理三、技术方案3.1、下单接口扩展3.3.1、Request类新增deviceType3.3.2、申请单新增字段产品策略(productStrategy)3.3.3、下单产品策略的处理逻辑3.2、询价模块的设计3.2.1、Context设计3.2.2、ProductStrategy类设计3.2.2.1、AbstractProductS…

k8s篇之概念介绍

文章目录时光回溯什么是K8SK8S不是什么一、K8S构成组件控制平面组件&#xff08;Control Plane Components&#xff09;kube-apiserveretcdkube-schedulerkube-controller-managercloud-controller-managerNode 组件kubeletkube-proxy容器运行时&#xff08;Container Runtime&…

Spring Cloud Nacos实战(七)- Nacos之Linux版本安装

Nacos之Linux版本安装 Linux版NacosMySql生产环境配置 ​ 已经给大家讲解过了Nacos生产环境下需要搭建集群配置&#xff0c;那么这里我们预计需要&#xff1a;1个Nginx3个Nacos注册中心1个MySql 具体配置&#xff1a; 在官网上下载NacosLinux版本&#xff1a;https://github…

基于SSM框架的CMS内容管理系统的设计与实现

基于SSM框架的CMS内容管理系统的设计与实现 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目…