Bootstrap

js中【Worker】相关知识点详细解读

什么是 JavaScript 中的 Worker?

JavaScript 中的 Worker 是一个可以在后台线程中运行代码的 API,这样可以避免主线程(通常是 UI 线程)被阻塞。使用 Worker 时,JavaScript 可以在多线程环境中工作,解决了单线程的瓶颈问题。

通常情况下,JavaScript 是单线程的,也就是所有的代码(包括 DOM 操作和事件处理等)都在同一个线程里执行。如果某段代码需要大量计算,或者运行时间过长,会阻塞整个页面,导致界面卡顿甚至崩溃。Worker 提供了一种将复杂或耗时的任务分配到后台线程执行的方法。

Worker 的核心特点:

  1. 后台线程:Worker 代码在与主线程不同的后台线程中执行。
  2. 无阻塞:可以处理计算密集型任务而不阻塞主线程。
  3. 无 DOM 访问:Worker 不能直接访问主线程中的 DOM(文档对象模型),它们只能通过消息与主线程进行通信。
  4. 消息传递:主线程和 Worker 通过 postMessage()onmessage 事件来交换数据。

基本使用

JavaScript 中有几种 Worker,不同 Worker 的用法略有区别:

  1. Web Worker:标准的 Worker,用于网页中的后台线程。
  2. Service Worker:专门用于网络请求的 Worker。
  3. Shared Worker:可以被多个脚本或页面共享的 Worker。

下面我们先来重点讲解 Web Worker 的使用。


创建 Web Worker

你需要创建一个单独的 JavaScript 文件作为 Worker 的入口点,然后在主线程中启动 Worker。假设我们有一个需要在 Worker 中运行的文件 worker.js,并且有一个主线程脚本 main.js

1. 创建 Worker 文件

首先,我们编写一个 worker.js 文件,里面包含 Worker 将要执行的代码:

// worker.js
// Worker 内部不能访问 DOM,但可以执行任何复杂的计算任务
self.onmessage = function(event) {
    // 接收主线程发送的数据
    const number = event.data;

    // 进行耗时的计算任务,比如计算一个数的平方
    const result = number * number;

    // 将结果返回给主线程
    self.postMessage(result);
};

self 是 Worker 内部的全局对象,它相当于主线程中的 window,但没有 UI 相关的 API(比如不能操作 DOM)。onmessage 监听主线程发送的数据,而 postMessage 用于将结果返回给主线程。

2. 在主线程中创建 Worker

接下来,我们在 main.js 中创建并使用 Worker:

// main.js
// 创建一个新的 Worker 实例,指向 worker.js 文件
const worker = new Worker('worker.js');

// 主线程向 Worker 发送消息
worker.postMessage(10); // 发送数字 10,Worker 将计算 10 的平方

// 监听 Worker 返回的消息
worker.onmessage = function(event) {
    console.log('从 Worker 接收到的数据:', event.data); // 输出: 100
};

// 错误处理
worker.onerror = function(error) {
    console.error('Worker 出现错误:', error.message);
};

在这个例子中,postMessage() 方法用于从主线程向 Worker 发送数据,onmessage 方法用于监听 Worker 处理完任务后返回的数据。Worker 可以用于运行像计算密集型任务(如数学运算、数据处理)而不会影响主线程的流畅性。

3. 停止 Worker

当任务完成后,可以选择手动终止 Worker:

// 终止 Worker
worker.terminate();

一旦 terminate() 被调用,Worker 将立即停止执行,并且不会再处理任何任务。适当终止 Worker 可以节省系统资源。


使用 Worker 传递复杂数据

Worker 不仅可以处理简单的数据类型(如数字和字符串),还可以传递更复杂的数据(如对象和数组)。这里我们扩展上面的例子,传递一个对象到 Worker 并让 Worker 处理它:

// worker.js
self.onmessage = function(event) {
    const data = event.data;
    
    // 假设传入的是一个对象,我们对它的属性进行处理
    const result = {
        squared: data.number * data.number,
        doubled: data.number * 2
    };
    
    // 返回结果给主线程
    self.postMessage(result);
};
// main.js
const worker = new Worker('worker.js');

// 向 Worker 发送一个对象
worker.postMessage({ number: 5 });

// 监听 Worker 返回的对象
worker.onmessage = function(event) {
    const result = event.data;
    console.log('平方:', result.squared);   // 输出: 25
    console.log('翻倍:', result.doubled);  // 输出: 10
};

Worker 的局限性

  1. 无法访问 DOM:Worker 不能直接操作 DOM。如果你需要进行与 UI 相关的操作,必须通过 postMessage() 与主线程通信。
  2. 同源策略:Worker 脚本必须与页面脚本同源,不能跨域加载 Worker 文件。
  3. 性能开销:虽然 Worker 可以提高性能,但创建 Worker 本身也有一定的开销,特别是创建大量 Worker 时会增加资源消耗。
  4. 脚本文件:创建 Worker 必须通过外部文件,不能直接在同一个脚本内定义 Worker 的代码。

实际场景中的应用

  1. 处理大数据:Worker 常用于数据密集型计算(如图像处理、文件解析、复杂算法)。
  2. 保持 UI 流畅:在不阻塞用户界面的情况下执行长时间运行的任务。
  3. 实时数据处理:通过 Worker 进行后台的数据处理、文件操作等,不会影响页面的响应速度。
示例:使用 Worker 处理大量数据
// worker.js
self.onmessage = function(event) {
    const data = event.data;
    const processedData = data.map(item => item * 2); // 模拟处理大量数据
    self.postMessage(processedData);
};
// main.js
const worker = new Worker('worker.js');

// 模拟大量数据
//'-'是占位符,表示不关心这个参数的值,它是Array.from方法的第一个参数,即生成数组时当前元素的值
//i是第二个参数,表示当前数组元素的索引
const largeArray = Array.from({ length: 1000000 }, (_, i) => i);

worker.postMessage(largeArray);

worker.onmessage = function(event) {
    console.log('处理完成的数据:', event.data); // 将显示处理后的数据
};

在这个例子中,我们生成了一个包含百万个元素的数组并交给 Worker 处理。处理完成后,Worker 将返回一个新的数组给主线程。


总结

JavaScript 中的 Worker API 是一种强大的工具,能够帮助开发者处理耗时任务,避免页面卡顿。它主要通过后台线程执行代码,避免阻塞主线程,并通过消息传递与主线程进行通信。

关键点回顾:
  • Worker 是运行在后台线程中的脚本,主线程与 Worker 通过 postMessage()onmessage 通信。
  • Worker 不能访问 DOM,但适合用于计算密集型任务。
  • 创建 Worker 有一定开销,建议合理使用,尤其是在多 Worker 环境下。

可以尝试将复杂计算任务移到 Worker 中,这样可以提高用户体验并保持应用的流畅性。

;