FIber + webWorker

news/2024/11/23 9:26:05/

文章目录

  • Fiber
    • 主要功能
    • 解决的问题
    • 如何解决
  • webworker 多线程
      • 作用
      • 使用注意点
      • 使用
        • 1 主线程
        • 3 Worker 加载脚本
        • 4 错误处理
        • 5 关闭 Worker
      • 数据通信

Fiber

主要功能

  • 为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新执行优先级低的任务
  • 增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行
  • dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行

从架构角度来看,Fiber 是对 React核心算法(即调和过程)的重写

从编码角度来看,FiberReact内部所定义的一种数据结构,它是 Fiber树结构的节点单位,也就是 React 16 新架构下的虚拟DOM

一个 fiber就是一个 JavaScript对象,包含了元素的信息、该元素的更新操作队列、类型。

解决的问题

JavaScript引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待

如果 JavaScript 线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿

如果组件较大,那么js线程会一直执行,然后等到整棵VDOM树计算完成后,才会交给渲染的线程;这就会导致一些用户交互、动画等任务无法立即得到处理,导致卡顿的情况

如何解决

Fiber把渲染更新过程拆分成多个子任务,每次只做一小部分,做完看是否还有剩余时间,如果有继续下一个任务;如果没有,挂起当前任务,将时间控制权交给主线程,等主线程不忙的时候在继续执行;

即可以中断与恢复,恢复后也可以复用之前的中间状态,并给不同的任务赋予不同的优先级,其中每个任务更新单元为 React Element 对应的 Fiber节点

实现的上述方式的是requestIdleCallback方法

window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应

首先 React 中任务切割为多个步骤,分批完成。在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间再进行页面的渲染。等浏览器忙完之后有剩余时间,再继续之前 React 未完成的任务,是一种合作式调度。

该实现过程是基于 Fiber节点实现,作为静态的数据结构来说,每个 Fiber 节点对应一个 React element,保存了该组件的类型(函数组件/类组件/原生组件等等)、对应的 DOM 节点等信息。

作为动态的工作单元来说,每个 Fiber 节点保存了本次更新中该组件改变的状态、要执行的工作。

webworker 多线程

作用

为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。

在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

**特点:**Worker线程一旦新建成功就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。

**优点:**有利于随时响应主线程的通信。

缺点: 比较耗费资源,不应该过度使用,一旦使用完毕,就应该关闭。

使用注意点

(1)同源限制

分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

(2)DOM 限制

Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用documentwindowparent这些对象。但是,Worker 线程可以navigator对象和location对象。

(3)通信联系

Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

(4)脚本限制

Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

(5)文件限制

Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

使用

1 主线程

主线程采用new命令,调用Worker()构造函数,新建一个 Worker 线程。

javascript">> var worker = new Worker('work.js');

Worker()构造函数的参数是一个脚本文件,该文件就是 Worker 线程所要执行的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功(比如404错误),Worker 就会默默地失败。

然后,主线程调用worker.postMessage()方法,向 Worker 发消息。

javascript">> worker.postMessage('Hello World');
> worker.postMessage({method: 'echo', args: ['Work']});

worker.postMessage()方法的参数,就是主线程传给 Worker 的数据。它可以是各种数据类型,包括二进制数据。

接着,主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。

javascript">> worker.onmessage = function (event) {
> console.log('Received message ' + event.data);
> doSomething();
> }
> 
> function doSomething() {
> // 执行任务
> worker.postMessage('Work done!');
> }

上面代码中,事件对象的data属性可以获取 Worker 发来的数据。

Worker 完成任务以后,主线程就可以把它关掉。

javascript">> worker.terminate();
> ```#### 2 Worker 线程Worker 线程内部需要有一个监听函数,监听`message`事件。```javascript
> self.addEventListener('message', function (e) {
> self.postMessage('You said: ' + e.data);
> }, false);

上面代码中,self代表子线程自身,即子线程的全局对象。因此,等同于下面两种写法。

javascript">> // 写法一
> this.addEventListener('message', function (e) {
> this.postMessage('You said: ' + e.data);
> }, false);
> 
> // 写法二
> addEventListener('message', function (e) {
> postMessage('You said: ' + e.data);
> }, false);

除了使用self.addEventListener()指定监听函数,也可以使用self.onmessage指定。监听函数的参数是一个事件对象,它的data属性包含主线程发来的数据。self.postMessage()方法用来向主线程发送消息。

根据主线程发来的数据,Worker 线程可以调用不同的方法,下面是一个例子。

javascript">> self.addEventListener('message', function (e) {
> var data = e.data;
> switch (data.cmd) {
>  case 'start':
>    self.postMessage('WORKER STARTED: ' + data.msg);
>    break;
>  case 'stop':
>    self.postMessage('WORKER STOPPED: ' + data.msg);
>    self.close(); // Terminates the worker.
>    break;
>  default:
>    self.postMessage('Unknown command: ' + data.msg);
> };
> }, false);

上面代码中,self.close()用于在 Worker 内部关闭自身。

3 Worker 加载脚本

Worker 内部如果要加载其他脚本,有一个专门的方法importScripts()

javascript">> importScripts('script1.js');

该方法可以同时加载多个脚本。

javascript">> importScripts('script1.js', 'script2.js');
4 错误处理

主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的error事件。

javascript">> worker.onerror(function (event) {
> console.log([
>  'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
> ].join(''));
> });
> 
> // 或者
> worker.addEventListener('error', function (event) {
> // ...
> });

Worker 内部也可以监听error事件。

5 关闭 Worker

使用完毕,为了节省系统资源,必须关闭 Worker。

javascript">> // 主线程
> worker.terminate();
> 
> // Worker 线程
> self.close();

数据通信

主线程与 Worker 之间通信时的内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。

主线程与 Worker 之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer 等类型,也可以在线程之间发送。

但是,拷贝方式发送二进制数据,会造成性能问题。比如,主线程向 Worker 发送一个 500MB 文件,默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫做Transferable Objects。这使得主线程可以快速把数据交给 Worker,对于影像处理、声音处理、3D 运算等就非常方便了,不会产生性能负担。

如果要直接转移数据的控制权,就要使用下面的写法。

javascript">> // Transferable Objects 格式
> worker.postMessage(arrayBuffer, [arrayBuffer]);
> 
> // 例子
> var ab = new ArrayBuffer(1);
> worker.postMessage(ab, [ab]);

http://www.ppmy.cn/news/1549262.html

相关文章

《Shader入门精要》基础纹理

使用Unity内置函数 之前的例子中我们都是手动去获取光源方向和视角方向,使用: 使用normalize(_WorldSpace LightPos0.xyz)来得到光源方向(这种方法实际只适用于平行光)​ 使用normalize(_WorldSpace CameraPos.xyz -i.worldPosit…

windows C#-异步返回类型(上)

异步方法可以具有以下返回类型&#xff1a; Task(对于执行操作但不返回任何值的异步方法)。Task<TResult>(对于返回值的异步方法)。void(对于事件处理程序)。任何具有可访问的 GetAwaiter 方法的类型。 GetAwaiter 方法返回的对象必须实现 System.Runtime.CompilerServi…

使用 cnpm 安装 Electron,才是正确快速的方法

当然&#xff0c;下面是总结的几种安装 Electron 的方法&#xff0c;包括使用 npm 和 cnpm&#xff0c;以及一些常见的问题解决技巧。 ### 1. 使用 npm 安装 Electron #### 步骤 1: 初始化项目 在你的项目目录中初始化一个新的 Node.js 项目&#xff1a; bash npm init -y …

Makefile 之 自动化变量

作用范围只在这条规则以及连带规则中&#xff0c;所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。 "$" 表示目标的集合&#xff0c;就像一个数组&#xff0c;"$"依次取出目标&#xff0c;并执于命令。 "$<"和"$&qu…

TSmaster Measurement setup(测量设置)

文章目录 1、Measurement setup功能介绍2、数据流过滤3、Measurement Filter 测量过滤器3.1 插入过滤器3.2 设置过滤数据3.3 过滤条件的失能3.4 窗口缩放 1、Measurement setup功能介绍 Measurement setup 窗体主要包含三个功能&#xff1a; 提供一个面板&#xff0c;用户能够…

使用IDE实现java端远程调试功能

使用IDE实现java端远程调试功能 1. 整体描述2. 前期准备3. 具体操作3.1 修改启动命令3.2 IDE配置3.3 打断点3.4 运行Debug 4. 总结 1. 整体描述 在做项目时&#xff0c;有些时候&#xff0c;需要和第三方进行调式&#xff0c;但是第三方不在一起&#xff0c;需要进行远程调试&…

Java求职招聘网站开发实践

一、项目介绍 本文将介绍如何使用Java技术栈开发一个求职招聘网站。该网站主要实现求职者和招聘方的双向选择功能&#xff0c;包含用户管理、职位发布、简历投递等核心功能。 二、技术选型 后端框架&#xff1a;Spring Boot 2.7.0数据库&#xff1a;MySQL 8.0前端框架&#…

2025蓝桥杯(单片机)备赛--扩展外设之UART1的原理与应用(十二)

一、串口1的实现原理 a.查看STC15F2K60S2数据手册: 串口一在590页,此款单片机有两个串口。 串口1相关寄存器: SCON:串行控制寄存器(可位寻址) SCON寄存器说明: 需要PCON寄存器的SMOD0/PCON.6为0,使SM0和SM1一起指定工作模式,这里选择工作模式1,REN位置1,允许接受, …