前言
最近在优化公司的一个项目,使用的就是web worker去优化,做了那些优化,一个是状态的优化,(通信的状态实时更新,以前的做法是做个定时任务实时获取它的状态,然后让它在页面渲染,这样就会造成了,一个是定时任务,实时获取,一个是一直在不断的渲染,虽然肉眼看不出什么,但是这样会造成一个主进程的负担非常大,然后我就引用了web worker开一个进程给它,定时去获取,然后在做判断是否与前面的状态是否一致,一致不传入主进程,不一致传入,然后这样就可以页面的明显的比较丝滑了)还有一个就是做一个计算,涉及到了比较复杂的数据,然后也是在web worker计算好了在传入主线程。最后用来做文件的下载,因为我们的文件涉及到了视频的下载,有些视频几个G的下载,那就是用了这个来处理。这篇文章呢,本来打算自己写的,然后做总结,然后自己比较懒,发现GPT写的比我还全面。当然我也参考了其它文章来看是否写的正常,对比MDN.
Web Worker 是一项 HTML5 标准中的特性,可以在 Web 页面中创建多个 JavaScript 线程,从而实现多线程并行执行代码的效果。它是为了解决 JavaScript 在单线程下的并发执行问题而出现的。使用 Web Worker 可以使 UI 界面保持流畅和响应性,并有效地利用计算机的硬件资源,提高 Web 应用程序的性能和响应速度。
Web Worker 主要有两种类型:Dedicated Worker 和 Shared Worker。Dedicated Worker 是指只与一个页面相关联的 Worker,而 Shared Worker 则是可以被多个页面共享的 Worker。Worker 可以操作独立的数据副本,这些数据副本在线程之间通信时不会互相干扰,并可以使用 postMessage() 方法进行相互通信。
下面详细介绍 Web Worker 的相关知识点:
1.Web Worker 基本用法
Web Worker 最重要的作用是将一部分代码运行在另一个线程中,用以减轻主线程负荷,以达到提高网页性能的目的。
使用 Worker API 来创建一个后台工作者线程,语法如下:
// 创建一个 Worker 线程
var myWorker = new Worker('worker.js');
其中,worker.js 为后台线程所要执行的 JavaScript 文件。
2.Dedicated Worker 和 Shared Worker
Web Worker 分为两种类型:Dedicated Worker 和 Shared Worker。Dedicated Worker 是指只与一个页面相关联的 Worker,而 Shared Worker 则是可以被多个页面共享的 Worker。
Dedicated Worker 的创建方式比较简单,而 Shared Worker 的使用方法则比较复杂。例如,当多个页面同时使用同一个 Shared Worker 时,它们在访问该 Worker 时必须保证具有相同的域名和端口号。
3.postMessage() 和 onmessage 事件
Dedicated Worker 和主线程之间可以通过 postMessage() 方法来传递消息,在 Dedicated Worker 中可以使用 self 属性代替 this,其中 self.postMessage() 用于向主线程发送消息。
主线程接收后台 Worker 发送过来的信息,可以通过 onmessage 事件进行处理,代码如下:
// 主线程代码
// 创建 Worker
var myWorker = new Worker('worker.js');
// 接收来自 Worker 的消息并进行处理
myWorker.onmessage = function(event) {
console.log('Received message from worker:', event.data);
};
4.线程与锁
由于 JavaScript 实现不支持锁,因此 Web Worker 的实现也没有锁概念。但可以借助 MessageChannel API 来模拟锁,即将信道分成互斥的“写入键”和“读取键”,从而控制对数据结构的访问。
5.限制和局限性
Web Worker 在实际使用时也存在一些限制和局限性,例如:
- Web Worker 脚本不能访问 DOM。
- Web Worker 不能从主线程中调用函数或方法。
- 所有的 worker 线程必须遵守同源策略。
6.发送和接收二进制数据
Web Worker API 允许在主线程和后台线程之间交换二进制数据,以及共享 ArrayBuffer 和 MessagePort 对象。可以使用 postMessage() 方法发送 ArrayBuffers、TypedArrays 和 DataViews。 示例代码如下:
// 计算行列式
function determinant(matrix) {
var length = matrix.length;
if (length === 2) {
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
} else {
var result = 0;
var cofactor = 1;
for (var i = 0; i < length; i++) {
var minor = [];
for (var j = 1; j < length; j++) {
minor.push(matrix[j].slice(0, i).concat(matrix[j].slice(i + 1)));
}
result += cofactor * matrix[0][i] * determinant(minor);
cofactor = -cofactor;
}
return result;
}
}
// 主线程
var myWorker = new Worker('worker.js');
myWorker.onmessage = function(event) {
console.log('Received worker message:', event.data);
};
// 创建 ArrayBufer,这里是一个 4x4 的矩阵
var buffer = new ArrayBuffer(64);
var view = new Int32Array(buffer);
for (var i = 0; i < 16; i++) {
view[i] = i;
}
// 向 worker 发送 ArrayBuffer
myWorker.postMessage(buffer, [buffer]);
错误处理
在 Web Worker 中,当代码执行出现错误时,会抛出一个相应的异常。Worker 可以通过将错误对象传递给主线程来报告错误。示例代码如下:
// 后台 Worker 代码示例,计算阶乘
self.onmessage = function(event) {
var n = event.data;
if (n < 0) {
self.postMessage('Error: argument must be non-negative');
} else {
var result = 1;
for (var i = 2; i <= n; i++) {
result *= i;
}
self.postMessage(result);
}
};
8.性能优化
使用 Web Worker 进行性能优化时,可以考虑以下几点:
- 确保创建 Worker 的开销不要过大。
- 尽可能重用 Worker 实例,而不是在每次需要多线程执行时都新建一个 Worker 对象。
- 调整 Worker 线程数量和运行策略,常见的有按需启动、固定数量和循环调度等。
9. 消息传递
Web Worker 之间通过消息传递进行通信。可以使用 postMessage() 和 onmessage 属性在主线程和 Worker 之间发送和接收消息。示例代码如下:
// 主线程
var myWorker = new Worker('worker.js');
myWorker.onmessage = function(event) {
console.log('Received worker message:', event.data);
};
myWorker.postMessage(42);
// 后台 Worker 代码示例
self.onmessage = function(event) {
console.log('Received message from main thread:', event.data);
self.postMessage('Hello, main thread!');
};
10.终止\关闭 Worker
可以使用 terminate() 方法终止 Worker 线程。示例代码如下:
// 主线程
var myWorker = new Worker('worker.js');
myWorker.postMessage(42);
// 等待 2 秒后终止 Worker 线程
setTimeout(function() {
myWorker.terminate();
console.log('Worker terminated.');
}, 2000);
// 后台 Worker 代码示例
self.onmessage = function(event) {
console.log('Received message from main thread:', event.data);
};
11. Web Worker API
除了上面提到的方法和属性,Web Worker 还提供了一些其他的 API,包括:
- importScripts():在 Worker 中加载脚本。
- close():关闭 Worker 线程。
- XMLHttpRequest:在 Worker 中发起网络请求。
- WorkerGlobalScope:Worker 全局作用域,不同于浏览器中的全局作用域。
- navigator、location、console 等对象:这些对象在 Worker 中与主线程中的对象略有差异,最突出的是它们不支持 DOM 操作。
12 .Worker 线程安全
Worker 线程执行的代码必须是线程安全的,因为它们在多个线程中同时运行。具体来说,Worker 线程不能访问主线程的 DOM、BOM 或 JavaScript 对象,而是操作自己的局部变量和引入的脚本库。由于共享内存的特性,如果多个 Worker 同时读写同一个共享变量,可能会导致数据竞争和不可预期的结果。
13.使用 SharedArrayBuffer 共享内存
SharedArrayBuffer 是 HTML5 的新增特性,可以在多个 Worker 之间共享内存。与普通数组不同,SharedArrayBuffer 的操作是原子性的,能够保证多个线程同时读写 SharedArrayBuffer 不会出现竞争和冲突。
使用 SharedArrayBuffer 需要注意以下问题:
- SharedArrayBuffer 必须显式地分配指定长度的空间。
- 可以使用 TypedArray(如 Int8Array、Float32Array 等)对 SharedArrayBuffer 进行操作。
- 需要注意内存一致性问题,即各个线程操作相同的内存区域时需要协调好读写的先后顺序和同步机制,避免读取到不一致的数据。
14. Web Worker 应用实例
Web Worker 可以用于许多场景,这里举几个实际应用的例子:
- 图像处理:对于大型高分辨率图像,可以使用 Worker 将其切分为多个小部分,并利用多核 CPU 并行处理各自分片,以提高计算效率。
- 负载均衡:可以使用 Worker 平均分配服务器请求任务,避免某一线程负载过高。
- 实时通信:可以使用 Worker 处理客户端与服务端的双向通信,以协调客户端发送和接收消息的操作。
15.Web Worker 的限制
Web Worker 主要有以下几个限制:
- 无法访问 DOM:Worker 线程运行在与主线程不同的上下文环境中,无法访问主线程的 DOM 对象。
- 不能使用 alert()、prompt() 和 confirm() 方法:这些方法是阻塞主线程的,但是在 Worker 线程调用会导致浏览器崩溃。
无法访问 window 和 document 对象:因为 Worker 线程没有 window 和 document 对象,所以它们无法访问这些对象。 - 无法使用某些 JavaScript API:比如不能使用 localStorage 和 sessionStorage,因为它们都依赖于 window 对象。
- 必须遵守同源策略:Worker 线程必须从与其本身代码文件相同的域名下加载其他脚本文件。
16.Web Worker 的兼容性问题
Web Worker 是 HTML5 中新增的特性,需要浏览器支持。目前绝大多数主流浏览器都支持 Web Worker,但是还有一些老版本的浏览器可能不支持。
为了解决兼容性问题,可以使用 Modernizr 库,检测当前浏览器是否支持 Web Worker,并提供相应的替代方案。
17.Web Worker 的调试技巧
由于 Worker 线程不能使用 alert()、console.log() 和调试工具,因此在调试 Web Worker 时可能比较困难。下面是一些调试技巧:
- 在主线程中使用 console.log() 对消息进行调试:可以在后台 Worker 代码中发送和接收消息,在主线程中通过 console.log() 输出消息内容进行调试。
- 使用 postMessage() 将错误信息传递回主线程:可以在 Worker 端捕获错误,并通过 postMessage() 方法将错误信息发送给主线程,同时在主线程中输出错误信息。
- 利用浏览器开发工具:可以在 Chrome 和 Firefox 浏览器的开发者工具中查看 Worker 运行情况,例如 CPU 占用率、内存占用率等。
18.其它
Web Worker 还可以用于许多其他情况,例如:
- 大规模数据处理:对于需要进行复杂数学计算或其他计算密集型操作的大规模数据集,可以使用多个 Worker 在后台并行处理,加快计算速度和提高性能。
- 实现离线缓存:通过在 Worker 中缓存 Web 应用所需的静态资源,即使用户处于断网状态,也可以优化 Web 应用的体验,减少页面加载时间。
- 计时器和定时器操作:利用两个 Worker 计时器之间的双向消息传递,可以轻松实现精确的定时器操作,而不会受到主线程的占用和干扰。
总之,Web Worker 是一个非常强大且有用的工具,可以大幅提高 JavaScript 程序的性能和可维护性。但是一定要注意避免共享内存等问题,保证程序的正确性和安全性。
对比文章:(Exploring The Potential Of Web Workers For Multithreading On The Web)[https://www.smashingmagazine.com/2023/04/potential-web-workers-multithreading-web]
当然国内也有人去进行了一个翻译:译文
web worker使用:这篇第二种方式是有点问题的,不支持es6以上的语法,所以还是推荐使用worker-load
这篇:融会贯通
github也有人进行了一个简单的封装可以看看他:gitHub web worker