Web Worker

在传统 JavaScript 运行环境(单线程)中,所有任务都在主线程执行,包括 UI 渲染、事件处理和 JavaScript 代码执行。如果某个任务运行时间过长,会导致页面卡顿或无响应 😣。

Web Worker 是 HTML5 引入的一项技术,允许 JavaScript 代码在后台线程中运行,而不会阻塞主线程(UI 线程)。Web Worker 运行在独立的全局作用域,不会与主线程共享作用域。它主要用于执行计算密集型任务,如数据处理、大型计算或后台同步,以提高网页的性能和响应速度 🚀。

Web Worker 允许将某些任务分配给后台线程执行,主线程可以继续处理 UI 和用户交互。主线程与 Worker 通过消息传递(postMessage)进行通信。

创建Web Wroker

要使用 Web Worker,你需要创建一个独立的 JavaScript 文件,并在主线程中使用 Worker 构造函数加载它

const wroker = new Worker(path, options);
参数
说明

path

有效的 js 脚本,必须遵守同源策略。无效的 js 地址或者违反同源策略,会抛出SECURITY_ERR 类型错误

option.type

可选,用以指定 worker 类型。该值可以是 classicmodule。 如未指定,将使用默认值 classic

options.credentials

可选,用以指定 worker 凭证。该值可以是 omit, same-origin,或 include。如果未指定,或者 type 是 classic,将使用默认值 omit (不要求凭证)

options.name

可选,在 DedicatedWorkerGlobalScope 的情况下,用来表示 worker 的 scope 的一个 DOMString 值,主要用于调试目的。

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

主线程与 worker 线程都是通过 postMessage 方法发送消息,以及监听 message 事件来接收消息。

const worker = new Worker("/worker.js");

// 接收 worker 线程发送的消息
worker.addEventListener("message", (ev) => {
  console.log(ev.data);
});

// 向worker线程发送消息
worker.postMessage("Hello worker.js 👋");

// worker.js
self.addEventListener("message", (ev) => {
  console.log(ev.data); // Hello 👋
  self.postMessage("Hell Main.js 🎉"); // 向主线程发送消息
});

主线程与 worker 之间的数据传递是传值,而不是地址,即使你传递的是一个 Object,并且被直接传递回来,接收到的也不是原来的那个值。

监听错误消息

web worker 提供了两个事件监听错误,error 和 messageerror。

事件
描述

error

当 worker 内部出现错误时触发

messageerror

message 事件接收到无法被反序列化的参数时触发

关闭 worker 线程

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

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

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

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

worker 线程引入其他 js 文件

我们可以在 worker 线程中利用 importScripts() 方法加载我们需要的 js 文件,而且,通过此方法加载文件不受同源策略约束!

ESModule 模式

主线程与 worker 之间可以传递哪些数据类型

数据是复制过去的,无法共享,且克隆大数据对象会影响性能。

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

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

  • Error 以及 Function 对象

  • DOM 节点

  • 对象的某些特定参数不会保留

结构化克隆短发支持的数据类型

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

类型
说明

symbols 除外

Boolean 对象

String 对象

lastIndex 字段不会被保留。

这基本上意味着所有的 类型化数组 ,如 Int32Array 等。

仅包括普通对象(如对象字面量)

多 Worker 线程管理

创建多个 Worker

Web Worker 并不限于单个线程,可以创建多个 Worker 并行执行任务,提高 CPU 利用率。

线程池(Worker Pool)

由于创建 Worker 需要额外的开销(如内存占用、线程管理),通常会使用线程池(Worker Pool),限制 Worker 数量,动态调度任务。

Web Worker 性能优化

  • 避免频繁创建/销毁 Worker:Worker 的创建、销毁都有开销,应尽量复用

  • 使用 Transferable Objects 传递大数据:避免 postMessage 复制数据导致的性能损耗:

合理分配任务

轻量任务(如计时器、事件监听) → 主线程

重计算任务(如图像处理、数据分析) → Web Worker

I/O 密集任务(如网络请求) → 异步 fetch/XMLHttpRequest

Web Worker 进阶通信

Web Worker 依赖 postMessage 进行数据传输,但不同数据类型的传输方式和性能有所不同。

复制数据传输(Structured Cloning)

默认情况下,postMessage 采用结构化克隆算法(Structured Clone Algorithm),支持传输以下数据类型: ✅ 基本类型(Number、String、Boolean) ✅ 对象(Object、Array) ✅ MapSetArrayBufferBlobFileFunctionDOM 节点Error 无法传输

示例:

⚠️ 缺点:数据是复制过去的,无法共享,且克隆大数据对象会影响性能。


共享内存(Transferable Objects)

为了解决数据复制的开销,Web Worker 允许使用 可转移对象(Transferable Objects),如 ArrayBuffer,在主线程和 Worker 之间零拷贝传输。

示例:

💡 可转移对象特点

  • 传输后,原对象在发送端不可用(byteLength 变为 0)。

  • 适用于大数据处理,减少性能损耗。


使用 MessageChannel 进行双向通信

MessageChannel 提供两个端口 port1port2,可在不同线程间建立双向通信

示例:

💡 适用于多个 Worker 之间的消息传递,比 postMessage 更灵活。

最后更新于

这有帮助吗?