FIber + webWorker

server/2024/11/25 17:15:43/

文章目录

  • 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/server/144850.html

相关文章

TCP为什么需要三次握手?两次握手或四次握手可以吗?

(1)三次握手可以保证双方具有接收和发送的能力 第一次握手服务端可以确认客户端的发送能力和服务端的接收能力是正常的;第二次握手客户端可以确认客户端和服务端的收发能力是正常的,但是服务端无法确认客户端的接收能力是正常的&…

使用php和Xunsearch提升音乐网站的歌曲搜索效果

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…

FastAPI和SQLModel结合的优点

FastAPI和SQLModel的结合为现代Web应用开发带来了一系列显著的优势,特别适合需要与SQL数据库交互的场景。以下是它们结合的一些主要优点: 简短的代码:SQLModel通过使用Python类型注解来定义数据模型,最小化代码重复,无…

原生微信小程序在顶部胶囊左侧水平设置自定义导航兼容各种手机模型

无论是在什么手机机型下,自定义的导航都和右侧的胶囊水平一条线上。如图下 以上图iphone12,13PRo 以上图是没有带黑色扇帘的机型 以下是调试器看的wxml的代码展示 注意:红色阔里的是自定义导航(或者其他的logo啊,返回之…

wpf处理C1FlexGrid 表格合计统计项处理,新增和查询都要生效

控件的两个事件都需要调用这个方法&#xff0c;新增的时候不写CellEditEnded&#xff0c;不然不会生效 ItemsSourceChanged"DetailGrid_ItemsSourceChanged" CellEditEnded"DetailGrid_ItemsSourceChanged" /// <summary> /// 列表1合计 …

蓝桥杯疑似例题解答方案(打印任意阶杨辉三角)

题目&#xff1a;输入n&#xff0c;打印n阶的杨辉三角 杨辉三角是一种特殊的由数字构成的三角形&#xff0c;边缘上的数字都是1&#xff0c;内部的数字则是左上角和右上角数字的加和。它本质上其实是二项展开的系数序列&#xff08;我们通过这个性质可以给出一种与本篇文章的方…

力扣-位运算-2【算法学习day.42】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…

从RNA测序数据中推断差异RNA编辑位点的统计推断

从RNA测序数据中推断差异RNA编辑位点的统计推断 描述 本仓库包含两个R函数,用于运行REDITs(RNA编辑测试)来调用差异RNA编辑位点: 在病例-对照(或条件1 vs 条件2)队列之间显著不同的RNA编辑位点 由REDIT-LLR(RNA编辑测试 - 对数似然比)处理显著与分类变量(例如性别、…