当前位置: 首页 > news >正文

Web Worker 与 SharedWorker 的介绍和使用

目录

      • 一、Web Worker
        • 1 Web Worker 是什么
        • 2 Web Worker 使用
        • 3 简单示例
      • 二、SharedWorker
        • 2.1 SharedWorker 是什么
        • 2.2 SharedWorker 的使用方式
        • 2.3 多页面数据共享的例子

一、Web Worker

1 Web Worker 是什么

Web Worker是 HTML5 标准的一部分,这一规范定义了一套 API。

Web Worker 允许我们在js主线程之外运行一个独立的线程(Worker),也就是运行在后台的一个独立的js脚本。因为Worker线程是独立的线程,与js主线程能够同时允许,互不阻塞。
所以如果有大量运算任务时,可以把任务交给Worker线程去处理,当Worker线程计算完成,再把结果返回给js主线程。这样,js主线程只用专注处理业务逻辑,不用耗费过多时间去处理大量复杂计算,从而减少了阻塞时间,也提高了运行效率,页面流畅度和用户体验自然而然也提高了。能解决的问题是:
1 解决页面卡死问题;
2 发挥多核CPU的优势,提高JS性能

2 Web Worker 使用

2.1 创建 worker

创建 worker 只需要通过 new 调用 Worker() 构造函数即可,它接收两个参数

const worker = new Worker(path, options);
参数说明
path有效的js脚本的地址,必须遵守同源策略。无效的js地址或者违反同源策略,会抛出SECURITY_ERR 类型错误
options.type可选,用以指定 worker 类型。该值可以是 classicmodule。 如未指定,将使用默认值 classic
options.credentials可选,用以指定 worker 凭证。该值可以是 omit, same-origin,或 include。如果未指定,或者 type 是 classic,将使用默认值 omit (不要求凭证)
options.name可选,在 DedicatedWorkerGlobalScope 的情况下,用来表示 worker 的 scope 的一个 DOMString 值,主要用于调试目的。

2.2 主线程与 worker 线程数据传递

发送消息:postMessage()。postMessage() 方法接收的参数可以是字符串、对象、数组等接受消息:监听 message 事件

2.3 监听错误

web worker 提供两个事件监听错误,errormessageerror

这两个事件的区别是:

事件描述
error当worker内部出现错误时触发
messageerrormessage 事件接收到无法被反序列化的参数时触发

2.4 关闭 worker 线程

worker 线程的关闭在主线程和 worker 线程都能进行操作,但对 worker 线程的影响略有不同。

// main.js(主线程)
const myWorker = new Worker('/worker.js'); // 创建worker
myWorker.terminate(); // 关闭worker
// worker.js(worker线程)
self.close(); // 直接执行close方法就ok了

无论是在主线程关闭 worker,还是在 worker 线程内部关闭 worker,worker 线程当前的 Event Loop 中的任务会继续执行。至于 worker 线程下一个 Event Loop 中的任务,则会被直接忽略,不会继续执行。

区别是,在主线程手动关闭 worker,主线程与 worker 线程之间的连接都会被立刻停止,即使 worker 线程当前的 Event Loop 中仍有待执行的任务继续调用 postMessage() 方法,但主线程不会再接收到消息。

在 worker 线程内部关闭 worker,不会直接断开与主线程的连接,而是等 worker 线程当前的 Event Loop 所有任务执行完,再关闭。也就是说,在当前 Event Loop 中继续调用 postMessage() 方法,主线程还是能通过监听message事件收到消息的。

详情例子可以阅读原文:XXXX

2.5 Worker 线程引用其他js文件

总有一些场景,需要放到 worker 进程去处理的任务很复杂,需要大量的处理逻辑,我们当然不想把所有代码都塞到 worker.js 里,那样就太糟糕了。不出意料,web worker 为我们提供了解决方案,我们可以在 worker 线程中利用 importScripts() 方法加载我们需要的js文件,而且,通过此方法加载的js文件不受同源策略约束

// utils.js
const add = (a, b) => a + b;
// worker.js(worker线程)
// 使用方法:importScripts(path1, path2, ...); importScripts('./utils.js'); // 加载需要的js文件console.log(add(1, 2)); // log 3

2.6 ESModule 模式

还有一些场景,当你开启一个新项目,正高兴的用 importScripts() 导入js文件时发现, importScripts() 方法执行失败。仔细一看,原来是新项目的 js 文件都用的是 ESModule 模式。难道要把引用到的文件都改一遍吗?当然不用,还记得上文提到初始化 worker 时的第二个可选参数吗,我们可以直接使用 module 模式初始化 worker 线程!

// main.js(主线程)
const worker = new Worker('/worker.js', {type: 'module'  // 指定 worker.js 的类型
});
// utils.js
export default add = (a, b) => a + b;
// worker.js(worker线程)
import add from './utils.js'; // 导入外部jsself.addEventListener('message', e => { postMessage(e.data);
});add(1, 2); // log 3export default self; // 只需把顶级对象self暴露出去即可

2.7 主线程和 worker 线程可传递哪些类型数据

很多场景,在调用某些方法时,我们将一些自定义方法当作参数传入。但是,当你使用 postMessage() 方法时这么做,将会导致 DATA_CLONE_ERR 错误。

// main.js(主线程)
const myWorker = new Worker('/worker.js'); // 创建workerconst fun = () => {};myWorker.postMessage(fun); // Error:Failed to execute 'postMessage' on 'Worker': ()=>{} could not be cloned.

那么,使用 postMessage() 方法传递消息,可以传递哪些数据?

postMessage() 传递的数据可以是由结构化克隆算法处理的任何值或 JavaScript 对象,包括循环引用。

结构化克隆算法不能处理的数据:

  • Error 以及 Function 对象;
  • DOM 节点
  • 对象的某些特定参数不会被保留
    • RegExp 对象的 lastIndex 字段不会被保留
    • 属性描述符,setters 以及 getters(以及其他类似元数据的功能)同样不会被复制。例如,如果一个对象用属性描述符标记为 read-only,它将会被复制为 read-write
    • 原形链上的属性也不会被追踪以及复制。

结构化克隆算法支持的数据类型:

类型说明
所有的原始类型symbols 除外
Boolean 对象
String 对象
Date
RegExplastIndex 字段不会被保留。
Blob
File
FileList
ArrayBuffer
ArrayBufferView这基本上意味着所有的 类型化数组 ,如 Int32Array 等。
ImageData
Array
Object仅包括普通对象(如对象字面量)
Map
Set

3 简单示例

主线程文件 webworker.html

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>Web Workers</title></head><body><button onclick="startWorker()">开启 worker 线程</button><button onclick="stopWorker()">关闭 worker 线程</button><script>var myWorker;function startWorker() {// 1 创建 workermyWorker = new Worker("./workers.js");// 2 向 worker 线程发送消息myWorker.postMessage("主线程向 worker 线程发送消息");// 3 接受 worker 线程发送的消息myWorker.addEventListener("message", (e) => {console.log("主线程收到的 worker 线程发来的数据:", e.data);});// 这种写法也可以 接收消息// myWorker.onmessage = e => {//    console.log(e.data);// };// 4 监听错误myWorker.addEventListener("error", (err) => {console.log(err.message); // 当worker内部出现错误时触发});myWorker.addEventListener("messageerror", (err) => {console.log(err.message); // 当 message 事件接收到无法被反序列化的参数时触发});}// 5 在主线程中关闭 worker 线程function stopWorker() {myWorker.terminate();console.log("关闭 worker 线程");myWorker = undefined;}</script></body>
</html>

worker 线程文件 worker.js

(function () {// 接受与发送消息self.addEventListener('message', e => { // 接收到消息console.log('worker 线程收到的消息:', e.data);self.postMessage('向主线程发送消息'); // 向主线程发送消息});// 监听错误self.addEventListener('error', err => {console.log(err.message);});self.addEventListener('messageerror', err => {console.log(err.message);});// 关闭 worker 线程// self.close(); // 直接执行close方法就ok了}
)()

二、SharedWorker

2.1 SharedWorker 是什么

SharedWorker 是一种特殊类型的 Worker,可以被多个浏览上下文访问,比如多个 windows,iframes 和 workers,但这些浏览上下文必须同源。它们实现于一个不同于普通 worker 的接口,具有不同的全局作用域:SharedWorkerGlobalScope ,但是继承自WorkerGlobalScope

2.2 SharedWorker 的使用方式

SharedWorker 线程的创建和使用跟 worker 类似,事件和方法也基本一样。 不同点在于,主线程与 SharedWorker 线程是通过MessagePort建立起链接,数据通讯方法都挂载在SharedWorker.port上。

值得注意的是,如果你采用 addEventListener 来接收 message 事件,那么在主线程初始化SharedWorker() 后,还要调用 SharedWorker.port.start() 方法来手动开启端口。

// main.js(主线程)
const myWorker = new SharedWorker('./sharedWorker.js');myWorker.port.start(); // 开启端口myWorker.port.addEventListener('message', msg => {console.log(msg.data);
})

但是,如果采用 onmessage 方法,则默认开启端口,不需要再手动调用SharedWorker.port.start()方法

// main.js(主线程)
const myWorker = new SharedWorker('./sharedWorker.js');myWorker.port.onmessage = msg => {console.log(msg.data);
};

以上两种方式效果是一样的,具体信息请参考MessagePort。

由于 SharedWorker 是被多个页面共同使用,那么除了与各个页面之间的数据通讯是独立的,同一个SharedWorker 线程上下文中的其他资源都是共享的。基于这一点,很容易实现不同页面之间的数据通讯。

2.3 多页面数据共享的例子

一个利用SharedWorker实现多页面数据共享的例子

  1. index 页面的 add 按钮,每点击一次,向 sharedWorker 发送一次 add 数据,页面 count 增加1
// index.html<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>index page</title></head><body><p>index page: </p>count: <span id="container">0</span><button id="add">add</button><br>// 利用iframe加载<iframe src="./iframe.html"></iframe></body><script type="text/javascript">if (!!window.SharedWorker) {const container = document.getElementById('container');const add = document.getElementById('add');const myWorker = new SharedWorker('./sharedWorker.js');myWorker.port.start();myWorker.port.addEventListener('message', msg => {container.innerText = msg.data;});add.addEventListener('click', () => {myWorker.port.postMessage('add');});}</script>
</html>
  1. iframe 页面的 reduce 按钮,每点击一次,向 sharedWorker 发送一次 reduce 数据,页面count 减少1
// iframe.html<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>iframe page</title></head><body><p>iframe page: </p>count: <span id="container">0</span><button id="reduce">reduce</button></body><script type="text/javascript">if (!!window.SharedWorker) {const container = document.getElementById('container');const reduce = document.getElementById('reduce');const myWorker = new SharedWorker('./sharedWorker.js');myWorker.port.start();myWorker.port.addEventListener('message', msg => {container.innerText = msg.data;})reduce.addEventListener('click', () => {myWorker.port.postMessage('reduce');});}</script>
</html>
  1. sharedWorker 在接收到数据后,根据数据类型处理 num 计数,然后返回给每个已连接的主线程。
// sharedWorker.jslet num = 0;
const workerList = [];self.addEventListener('connect', e => {const port = e.ports[0];port.addEventListener('message', e => {num += e.data === 'add' ? 1 : -1;workerList.forEach(port => { // 遍历所有已连接的part,发送消息port.postMessage(num);})});port.start();workerList.push(port); // 存储已连接的partport.postMessage(num); // 初始化
});

结果可以发现,index 页面和 iframe 页面的 count 始终保持一致,实现了多个页面数据同步。

相关文章:

Web Worker 与 SharedWorker 的介绍和使用

目录一、Web Worker1 Web Worker 是什么2 Web Worker 使用3 简单示例二、SharedWorker2.1 SharedWorker 是什么2.2 SharedWorker 的使用方式2.3 多页面数据共享的例子一、Web Worker 1 Web Worker 是什么 Web Worker是 HTML5 标准的一部分&#xff0c;这一规范定义了一套 API…...

React:Redux和Flux

React,用来构建用户界面,它有三个特点: 作为view,构建上用户界面虚拟DOM,目的就是高性能DOM渲染【diff算法】、组件化、多端同构单向数据流,是一种自上而下的渲染方式。Flux 在一个React应用中,UI部分是由无数个组件嵌套构成的,组件和组件之间就存在层级关系,也就是父…...

TypeScript 学习之Class

基本使用 class Greeter {// 属性greeting: string;// 构造函数constructor(message: string) {// 用this 访问类的属性this.greeting message;}// 方法greet() {return Hello, this.greeting;} } // 实例化 let greeter new Greeter(World);声明了一个Greeter类&#xff…...

doris - 数仓 拉链表 按天全量打宽表性能优化

数仓 拉链表 按天全量打宽性能优化现状描述优化现状描述 1、业务历史数据可以变更 2、拉链表按天打宽 3、拉链表模型分区字段设计不合理&#xff0c;通用的过滤字段没有作为分区分桶字段 4、拉链表表数据量略大、模型数据分区不合理和服务器资源限制&#xff0c;计算任务执行超…...

服务器虚拟化及优势

服务器虚拟化是从一台物理服务器创建多个服务器实例的过程。每个服务器实例代表一个隔离的虚拟环境。在每个虚拟环境中&#xff0c;都可以运行单独的操作系统。 1.更有效的资源调配 使用虚拟化技术大大节省了所占用的空间&#xff0c;减少了数据中心里服务器和相关硬件的数量。…...

华为ensp模拟校园网/企业网实例(同城灾备及异地备份中心保证网络安全)

文章简介&#xff1a;本文用华为ensp对企业网络进行了规划和模拟&#xff0c;也同样适用于校园、医院等场景。如有需要可联系作者&#xff0c;可以根据定制化需求做修改。作者简介&#xff1a;网络工程师&#xff0c;希望能认识更多的小伙伴一起交流&#xff0c;私信必回。一、…...

git命令篇(持续更新中)

首先介绍这个网页&#xff1a;https://learngitbranching.js.org/?localezh_CN --提交命令 git commit --创建分支 git branch <分支名> --切换分支 git checkout <分支名> --合并分支 (合并到主分支去&#xff0c;把我合并到谁的身上去) 自己写的分支合并到主线…...

用记事本实现“HelloWorld”输出

一、在任意文件夹中创建一个新的文本文档文件并写入以下代码 public class Hello{public static void main (String[] args){System.out.print("Hello,World!");} } 二、修改文件名称及文件类型为 Hello.java 特别注意&#xff1a;文件命名必须与代码中类的名称相同…...

Python基础1

1. 注释 单行注释&#xff1a;以#开头。一般建议注释和内容用空格隔开。 多行注释&#xff1a;以一对三个双引号括起来的内容是注释。“““示例注释”””。 2. 数据类型 验证数据类型的方法&#xff1a;type&#xff08;被查看类型的数据&#xff09;。 注意&#xff1a;…...

4.2 双点双向路由重发布

1. 实验目的 熟悉双点双向路由重发布的应用场景掌握双点双向路由重发布的配置方法2. 实验拓扑 双点双向路由重发布如图4-6所示: 图4-6:双点双向路由重发布 3. 实验步骤 IP地址的配置R1的配置 <Huawei>system-v…...

AcWing《蓝桥杯集训·每日一题》—— 3768 字符串删减

AcWing《蓝桥杯集训每日一题》—— 3768. 字符串删减 文章目录AcWing《蓝桥杯集训每日一题》—— 3768. 字符串删减一、题目二、解题思路三、代码实现本次博客我是通过Notion软件写的&#xff0c;转md文件可能不太美观&#xff0c;大家可以去我的博客中查看&#xff1a;北天的 …...

第五天笔记

1. 简述图片验证码使用流程&#xff1f; 1.前段生成UUID随机值&#xff0c;作为GET请求参数 2.后端试图进行判断&#xff0c;调用工具类来生成图片验证码和内容 3.将验证码内容使用redis保存到本地,前端传入的uuid作为key, 4.在前段输入获取到的图片验证码&#xff0c;想后端发…...

如何使用ArcGIS进行地理配准

1.概述 对于GIS数据而言&#xff0c;坐标信息是灵魂&#xff0c;有了坐标信息之后才能和别的数据结合使用&#xff0c;之前有介绍过矢量数据定义坐标信息的方法&#xff0c;针对栅格图&#xff0c;这里为大家介绍一下通过地理配准增加坐标信息的方法&#xff0c;希望能对你有所…...

【java基础知识】

Java中的基本数据类型是什么&#xff1f; byte&#xff1a;1字节&#xff0c;有符号&#xff0c;表示整数&#xff0c;范围为-128到127。short&#xff1a;2字节&#xff0c;有符号&#xff0c;表示整数&#xff0c;范围为-32768到32767。int&#xff1a;4字节&#xff0c;有符…...

Java提供了哪些IO方式? NIO如何实现多路复用?

第11讲 | Java提供了哪些IO方式&#xff1f; NIO如何实现多路复用&#xff1f; IO 一直是软件开发中的核心部分之一&#xff0c;伴随着海量数据增长和分布式系统的发展&#xff0c;IO 扩展能力愈发重要。幸运的是&#xff0c;Java 平台 IO 机制经过不断完善&#xff0c;虽然在某…...

人的大脑遇事的思考解决过程

人遇到问题的思考解决过程&#xff0c;大概如下&#xff1a;1&#xff09; 遇到问题&#xff1b;2&#xff09; 首先&#xff0c;不是直接推理&#xff0c;而是用直觉在自己的知识模式库里搜索&#xff0c;有没有相似的模式或者相同的模式。3&#xff09; 如果&#xff1a;3a)有…...

GNU zlib 压缩与解压文件详细介绍

GNU zlib 压缩与解压文件详细介绍 1.概述 zlib 模块为 GNU 项目的 zlib 压缩库中的许多函数提供了一个低级接口 2.使用内存数据压缩与解压 2.1.压缩与解压缩 使用 zlib 的最简单方法是将所有数据保存在内存中进行压缩或解压缩。 import zlib import binasciioriginal_dat…...

离线环境轻量级自动化部署

流程图&#xff1a; 常规系统发布的痛点 服务器频繁重启&#xff0c;上面部署的应用服务不能随之重启&#xff0c;导致服务时常宕机应用手动部署相对比较麻烦&#xff0c;步骤繁琐应用发布环境取决于发布人本地环境&#xff0c;导致不同发布人每次发布环境不一致&#xff0c;导…...

In-context Learning

formulate the example query -> LLM -> answerno gradient descent and fine-tuning, no parameters updateadvantages: 提供了与LLM进行交流的可解释的接口&#xff0c;通过template和demonstration将人类知识和LLM更好的结合&#xff1b;更像人类的预测思维&#xff…...

【新2023】华为OD机试 - 最优调度策略(Python)

华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 最优调度策略 题目 在通信系统中有一个常见的问题是对用户进行不同策略的调度 会得到不同系统消耗的性能 假设由 N 个待串行用户,每个用户可以使用 A/B/C 三种不同的调度策略 不同的策略会消耗不同的系…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...