IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Fiber 调度机制 -> 正文阅读

[JavaScript知识库]Fiber 调度机制

作者:温荣蛟
上篇提到为了解决任务调度粒度控制不够的问题,React引入了Fiber架构。Fiber将一个DOM更新任务拆分为由多个原子化可调度的节点组成的集合,从而提供了细粒度的任务调度能力。

Fiber这个名词并不是React的首创,和服务器端开发中的中纤程(Fiber)相同,后端开发中的纤程,也有着不同的名字,例如C/C++的协程、Java中的Loom(孵化中)、Go中的goroutine;这些不同名字的背后其实都是一个目的——通过线程复用降低线程的使用成本。

操作系统提供了进程和线程供用户进行使用,其中线程是操作系统调度的最小单位。但是线程的使用成本很高,主要体现在每个线程都是自己独立的内存空间、线程切换需要的控制时间很长。这就导致了当出现大量线程时,会引起操作系统的压力变大,从而消耗大量的资源,降低性能。因此,在服务端开发等需要用到大量线程的场景下,会通过一些机制对操作系统的线程进行复用,这个机制就是服务器端的Fiber.

但是在前端领域,js只提供了一个线程,不存在线程切换的场景。是不是就说明了Fiber机制无法应用到前端?React又为何将自身调度机制命名为Fiber?Fiber在前端领域的作用体现在什么方面?或者换句话说,前端Fiber的意义又是什么?

本章将从Fiber的结构入手,为读者揭开React Fiber的神秘面纱。接着,分析Fiber机制在Task调度过程中的运行机制,最后分析Fiber的意义。

第一节 Fiber架构
在这里插入图片描述
图2-1 Fiber架构图

如图2-1所示,Fiber架构由2部分组成:分发器(dispatcher)和调度器(scheduler)。分发器负责接受页面触发的事件,并将其转换为由 FiberNode组成的Task。交给调度器进行调度。调度器负责依据调度规则对Task进行细粒度的调度。

dispatcher: 它可以是一个组件的初始化挂载动作,也可以是某个事件触发的更新。

scheduler: 不管是挂载任务还是更新任务,都会推送到schedule调度器模块中,schedule模块将确定该任务的执行时机。

例子
在这里插入图片描述
图2-2 例子

图2-2展示了一个外卖页面的示意图,当用户点击加入购物车时,会触发该按钮的点击事件,将页面整体刷新为右图的样子。

当用户点击按钮时,触发点击事件,将事件会触发分发器的转换动作。可以先简单的认为分发器将触发4个FiberNode的更新,分别是:

删除“加入购物车”按钮;
增加“+”的圆形按钮;
增加“-”的圆形按钮;
增加一个计数文本“1”。

需要读者明确的是,本节提到的例子只是为了方便读者理解React Fiber机制。实际上,事件任务的拆分会依据一套更复杂的机制进行。并不是简单的按照显示的效果进行切分。

第二节 Task

分发器与调度器之间通过Task进行通信。Task对应的是组件的挂载或更新动作。由FiberRoot和FiberTree构成。

FiberRoot: 上述触发的挂载或则更新将初始化一个FiberRoot对象,是此次任务FiberTree的根节点。FiberRoot的currentTree上的stateNode属性指向触发本次事件的DOM节点,即FiberRoot节点链接了DOM节点和对应的任务节点。

FiberTree: 多个node节点组成。是事件涉及的所有节点的更新动作的集合。

2.1 FiberTree
在这里插入图片描述
图2-2 FiberTree示意图

如图2-2,FiberTree是由一个一个FiberNode以单链表的形式组合成的节点集合。FiberNode是调度器执行的最小单位,每执行完一个FiberNode更新后,线程的控制权将转交给调度器,由调度器来选择下一个执行的任务(继续或中断插入其他任务)。

第三节 分发器

第一节中提到分发器负责事件触发和更新,分发器除了处理事件的分发还需要负责节点的更新分发。当FiberNode进行更新操作时,分发器会根据FiberNode上的tag属性进行分发处理,不同的类型进入不同的更新逻辑。本文挑选3个最常用的向读者介绍。

3.1 HostComponent
宿主组件:它代表的是浏览器原生支持的html标签,如div/p/span…,该类型的节点需要处理的逻辑比较少,主要逻辑有:
1. 节点状态的标记
2. 文本域的特殊处理
3. 上下文切换

3.2 FunctionComponent
函数组件:开发者使用函数声明的组件。在hooks出现之前,函数组件一般作为纯组件出现,内部无数据交互。函数组件内部的主要逻辑是:

  1. 上下文切换
  2. 状态标记
  3. hooks执行
  4. 继续子节点更新或挂载

3.3 ClassComponent
类组件:使用class声明的组件。类组件和函数组件的区别在于,类组件提供了组件的声明周期,函数组件使用hooks实现类似功能。它的主要逻辑是:

  1. 上下文切换
  2. 状态标记
  3. 实例挂载
  4. 声明周期钩子执行
  5. 继续子节点的更新或挂载

第四节 Task调度过程

介绍了Task的组成,本节将会向读者介绍Task是如何参与调度器的两大重要过程的。

4.1任务启动
组件的初始化挂载或事件驱动更新都会触发一个任务的启动。如第二节所讲,创建一个新任务首先会创建一个FiberRoot,FiberRoot作为起点对FiberTree进行挂载或更新。分发器对FiberTree的每一个FiberNode分发更新组合成了一个Task,并且将该Task提交给调度器。调度器确定该任务的执行时机,当满足启动条件时,任务启动。

4.2任务挂起
任务进入调度器后,开始执行过程。在执行每个节点单元的时候会先判断浏览器是否有空余时间执行下一个工作单元。当不满足向下执行条件时(比如有其他高优先级的任务插入,或则当前时间片已用完),任务中断,被挂起。它的重新执行时机是由schedule模块控制,具体是怎么控制将在scheduler调度策略中细讲。

4.3任务恢复
任务执行过程中,每一个FiberNode的执行结果都会标记在属性上。调度模块重新执行挂起任务时,本质上是重新执行该更新任务,在具体更新每一FiberNode的时候会根据节点上的缓存属性判断该节点是否(props参数前后比对)需要重新计算更新数据,需要则逻辑不变,不需要则跳过。

4.4任务结束
当整个FiberTree中的节点都被处理后,统一提交所有FiberNode更新。并将更新结果统一反应到界面上,到此任务结束,任务将会被注销,同时回收FiberRoot保存的上下文。

第五节 React Fiber的意义

React Fiber本质上将js提供的一个工作线程进行复用,以实现对任务细粒度的控制。React Fiber和服务端开发的Fiber相比,都是通过精妙的设计改变当前线程正在执行的任务,从而实现线程复用。因此React将自身的调度机制命名为Fiber也是合理的。

前文已经说明了,js是单线程语言,不存在线程切换的成本,那么React设计的Fiber的意义就剩一个了:提高任务的调度效率。

5.1 响应时间和周转时间
在资源调度中,评价调度器效率的两个指标是响应时间(Response Time)和周转时间(Turnaround Time),两者的定义如下:

T周转时间=T完成时间-T到达时间
T响应时间=T首次运行-T到达时间
ATT(Average Turnaround Time)=(∑_N?〖T_周转时间 (n)〗)/N
ART(Average Response Time)=(∑_N?〖T_响应时间 (n)〗)/N

周转时间指的是任务进入系统到最终完成所经过的时间。平均周转时间则用来评价调度器的调度性能。考虑一个极端情况,T0时刻,ABC三个任务同时进入调度器,A任务需要4秒,B任务需要10s,C任务需要2s。若调度器按照BAC的顺序执行任务,ATT=(10+14+16)/3=13.33秒。若调度器按照CAB的顺序执行,则ATT=(2+6+18)/3=8.67秒。

响应时间指的是调度器第一次执行任务时所经过的时间,平均响应时间用来衡量调度系统响应任务的能力。对于交互多的系统而言,平均响应时间是一个非常重要的指标,毕竟用户不希望自己输入按下键盘后需要等待很长时间才能得到响应。

5.2 任务粒度
响应时间和周转时间构成了评价调度系统的两个重要指标,对这两个指标进行优化的一个有效手段是将一个大任务拆分为多个小任务,一次执行只执行一个小任务,小任务执行结束后,再根据调度策略执行另一个任务的小任务,通过轮转的方式,使得各个任务都能公平地获得执行,从而降低ATT和ART。
在这里插入图片描述
图2-2 任务切分

图2-2展示了一个任务切分后调度的一个示例。例子中使用的是最短执行时间优先的调度策略。不难看出,通过将大任务切分为小任务后进行调度,可以有效降低系统的平均周转时间和平均响应时间,大幅度提高了调度效率。但是本例中的最短执行时间优先的算法在实际中不可能被应用,因为在现实中很难事先获取任务的执行时间,React中有一些更精妙的调度策略,会在下一章进行详细讲述。

这种切分的本质是降低了任务的粒度,因此,任务的粒度很大程度上决定了调度的效率。任务粒度大的系统,无法有效地进行调度。

5.3 Fiber意义
的确,js没有提供多线程,即使任务切分后也无法利用多线程对任务进行加速,但这不意味着React的Fiber机制是无效的。

React Fiber的意义在于细化任务粒度,调度策略会影响不同场景下的调度效果,但这些调度策略的前提是任务粒度要足够小,否则任何调度策略都会遇到调度系统的系统瓶颈。

第六节 总结

本章梳理了Task建立到Task拆分再到挂起恢复的逻辑。从中不难发现Fiber架构对Task进行拆解,降低了任务阻塞的时长,对任务整体来说没有提速。

本章对Fiber的底层机制进行了分析,分析了任务粒度对调度的影响。在通过Fiber机制对任务进行细粒度的切分后,通过调度策略优化性能成为了可能,下一章将详细介绍调度器中的不同调度策略。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-09 18:14:27  更:2022-04-09 18:14:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:52:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码