查看原文
其他

深入理解 JavaScript Web Workers

小懒 FED实验室 2024-02-12
关注下方公众号,获取更多热点资讯

JavaScript 通常被描述为单线程语言。实际上,这指的是主线程,即你在浏览器看到的大多数工作中所使用的线程。这项工作涉及脚本、某些类型的渲染工作、HTML和CSS解析以及有助于改善用户体验的工作。事实上,浏览器确实使用其他线程来执行开发人员无法直接访问的工作,比如 GPU 线程。

浏览器依赖单线程来执行 JavaScript,这可能会导致一个常见问题。想象一下,您的网络应用程序运行平稳,直到一个繁重的 JavaScript 任务突然接管。由于主线程一次只能处理一个任务,因此它垄断了 CPU,导致网站反应迟钝,令用户失望。

解决方案是使用 Web Worker,这使得在 JavaScript 中注册和使用其他线程变得可能。

Web Worker 线程可以与主线程并行运行要求较高的任务,确保您的网站即使在复杂的操作过程中也能保持响应速度。本文首先对 Web Worker 做个简单的介绍,然后重点阐述如何使用 Web Worker 增强我们的应用。

1.Web Workers 介绍

Web Workers 是 JavaScript 对象,它们提供了一种在后台运行脚本而不影响主线程性能的方式。它们非常适用于执行计算密集型或耗时高的任务,确保用户界面保持响应。Web Workers 是一种宝贵的资源,但其创建成本相对较高。在可能的情况下,多个任务中重复使用它们。Web Workers 的一个关键限制是它们不能直接访问文档对象模型(DOM),因此它们非常适合不与 DOM 交互的任务。为了实现主线程和 Web Workers 之间的通信,提供了一些 API 以实现无缝的数据交换和协调。

在 Web Workers 的世界中,有几种可用的类型,每种类型都具有特定的用例:

  • Dedicated Worker:这种类型的 Web Worker 在各种浏览器中得到广泛支持,可用于大多数 Web 开发场景。
  • Shared Worker :共享 Worker 不像专用 Worker 一样得到普遍支持。在实施之前,需要检查目标浏览器是否支持它们。
  • Service Worker:Service Worker 主要用于创建渐进式 Web 应用程序(PWA)和处理离线功能。然而,与共享 Worker 一样,它们在不同的浏览器中的支持程度也不同,因此检查兼容性非常重要。
  • AudioWorklet:这种工作类型专门用于音频处理,也受到浏览器支持的差异。

本文将主要关注 Dedicated Worker,因为它们是在 Web 开发中得到广泛支持和经常被使用。

2.Dedicated Worker

Dedicated Worker 为同时执行脚本提供了简单的解决方案,允许我们将资源密集型任务从主线程中分离出来。下面通过一个简单的示例来详细说明如何有效地使用 Dedicated Worker:

// main.js
const worker = new Worker('worker.js');

// Sending a message to the worker
worker.postMessage('Hello from the main thread!');

// Listening for messages from the worker
worker.onmessage = function (event) {
  console.log(`Received a message from the worker: ${event.data}`);
};

在这段代码中,我们创建了一个新的 Worker 实例并将其存储在 worker 变量中。使用 postMessage() 函数,将消息从主线程传递给专用 Worker。同时还设置了一个 onmessage 事件监听器,以捕获和记录 Worker 发出的任何数据。

现在,让我们在一个独立的JavaScript文件(worker.js)中编写我们的 Web Workers 代码:

// worker.js
// Listen for messages from the main thread
self.onmessage = function (event) {
  const messageFromMain = event.data;
  console.log(`Received a message from the main thread: ${messageFromMain}`);

  // Perform some heavy computation
  const result = messageFromMain.toUpperCase();

  // Send the result back to the main thread
  self.postMessage(`Processed message: ${result}`);
};

代码非常容易理解。但是,您可能会注意到使用了 self 关键字。在 Web Workers 的上下文中,self 指的是指向 Worker 上下文的对象。这是一种可靠的引用 Worker 上下文的方式,与 this 关键字不同,在不同的情况下可能会表现出不可预测的行为。

使用 Web Workers 确实很简单。但是要记住一个关键点:不要使用脚本标签在主线程 HTML 中加载 Worker文件!而是在JavaScript代码中使用new Worker(path_to_the_worker_file) 动态加载它,就像示例中的 main.js 文件所示

3.Web Workers 中导入外部脚本

在某些情况下,您可能需要在 worker.js 文件中导入其他脚本。您可以使用 importScripts 函数实现这一点。以下是一个示例:

// worker.js
// Import an external script
importScripts('external-library.js');

// Now you can use functions and objects defined in external-library.js
const result = externalFunction(5); // Call a function from the imported script
console.log(`Result from the external function: ${result}`);

JavaScript 中的专用 Worker 提供了一种简单的并发执行脚本的方式,从而将资源密集型任务从主线程中分离出来。通过创建和 Web Workers 通信,即使在复杂操作期间,您也可以保持 Web 应用程序的响应性。

4.Web Workers 中的错误处理

Web Workers 提供了错误处理机制,以确保强壮可靠的执行。您可能会遇到两种主要类型的错误:

4.1.未处理的异常(Unhandled Exceptions)

如果 Web Workers 代码中发生未处理的异常,可能会导致意外的行为。为了捕获此类异常,可以在 Web Workers 的脚本中设置一个错误事件处理程序。以下是如何处理错误的示例:

self.addEventListener('error'function (event) {
  console.error('Error occurred in the web worker:', event.message);
  // You can perform custom error handling here
});

该事件监听器允许您捕获和记录发生在 Web Workers 上下文中的错误。

4.2.消息错误(Message Errors)

当您使用 postMessage 方法在主线程和 Web Workers 之间传输数据时,可能会发生另一种类型的错误。这些错误通常发生在尝试传输不受支持的数据类型或无法使用结构化克隆算法克隆的对象时。为了处理消息错误,可以在主线程中设置一个 messageerror 事件侦听器。以下是一个示例:

worker.postMessage('Some data'); // Trying to transfer data that might cause an error

worker.addEventListener('messageerror'function (event) {
  console.error('Message error:', event.message);
  // Handle the error gracefully
});

messageerror 事件允许您检测和处理主线程与 Web Workers 之间的数据传输相关的错误。

实现错误处理非常重要,可以确保您的 Web Workers 代码在出现异常或数据相关问题时仍然平稳可靠地运行。

5.Web Workers 基本原理

线程是不会自动终止的,除非您杀死它们!(像吸血鬼一样🧛) 在 Web Workers 的世界中,线程不会自动终止。不同于主线程在代码到达末尾时停止运行,Web Workers 会一直运行下去。要结束 Web Workers 线程,您需要在 Worker 的上下文中显式调用 self.close(),或者使用主线程的引用通过 worker.terminate() 终止。

5.1.内存使用

Web Workers 与其父线程(即主线程)共享内存。这意味着为 Web Workers 分配的内存空间与主线程分开但不是隔离的。在 Web Workers 中谨慎使用内存,因为过多的内存消耗可能会导致应用程序性能问题。

5.2.Web Workers 通信

与专用 Worker 的通信主要依赖于主线程的 postMessage() 方法。它允许您在主线程和 Worker 之间发送数据。但是,请注意,消息对象不是通过引用,而是通过值(它被复制)传递的。通过消息发送大量的数据可能会导致内存中的数据积累,从而影响性能。因此,在发送消息时考虑数据大小和结构。

5.3.所有权转移

从主线程向 Web Workers 转移数据的所有权是可能的,以避免数据重复。但是,请注意,数据实际上是传递而不是复制的。例如,当您将一个数组从主线程传递到 Web Workers 时,主线程对该数组的引用将变为空。但请记住,此功能仅适用于特定浏览器和特定数据类型(如ArrayBuffer,Blob和ImageBitmaps)。

5.4.一个有趣的用例

想象一下,您的网站有很多图片。当浏览器加载带有 <img /> 标签的网页时,它必须下载并将每个图像转换为位图,然后在屏幕上显示出来。这个解码过程发生在主线程中。如果由于某种原因,它花费了很长时间,可能会导致网站不响应和用户体验差。在这种情况下,您可以利用 Web Workers 来获取和解码图像,然后无缝地将位图传输到主线程。这种方法可以使您的网站保持响应,并提供更流畅的用户体验。

总结

Web Workers 提供了并行处理的能力,在这个不断变化的 Web 开发领域中,Web Workers 可能是开发者最好的秘密武器之一。我们需要在实践中不断去尝试、创新,并释放它们的潜力。

大家都在看

继续滑动看下一个

深入理解 JavaScript Web Workers

小懒 FED实验室
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存