Web Worker是一个可以在后台运行JavaScript的JavaScript API。它旨在允许执行复杂的计算而不会阻塞主线程。这对Web开发人员非常有用,但并没有广泛使用。本文将介绍如何使用Web Worker。
什么是Web Worker
众所周知,JavaScript语言的一个主要特点是其单线程性质,一次只能同步处理一个任务。这也是针对Node.js的后端开发者的主要批评点之一。
然而,JavaScript最初是有意设计为单线程语言,以适应当时的用途。
JavaScript的最初目标是通过操作DOM或BOM元素来促进Web页面与用户的交互。为了在这个上下文中追求效率,必须指定只有一个线程在任何时间点可以直接操作页面元素,以避免资源竞争和数据同步等问题,以确保系统的稳定性和安全性。
然而,JavaScript并不限制于线性任务处理。JavaScript具有消息队列和事件循环机制,通过异步消息处理实现并发处理。在高I/O并发事务处理中,不需要手动创建和销毁线程,也不需要额外的线程管理空间,因此性能优越。结果,探索在服务器端使用JavaScript的Node.js在高并发网络请求处理方面表现出色。
虽然JavaScript通过其异步机制有效地解决了与高I/O相关的性能问题,但其单线程 执行的基本特性仍然存在。这是因为在处理CPU密集型任务时无法充分利用其计算资源,这是因为它不能充分利用现代多核多线程机器的计算资源。
在现代大型前端项目中,随着代码复杂性的增加,本地计算密集型任务也变得需要。如果将JavaScript项目在单个线程中运行,应用程序可能会变得繁忙,忽略了频繁的用户交互,用户体验可能不佳。更严重的是,如果有太多的计算密集型任务,可能会导致资源饱和,使网页不再响应。因此,在Web项目中需要本地多线程计算能力,这就是Web Worker诞生的原因。
Web Worker作为HTML5的标准引入,官方定义如下:
Web Worker允许在与Web应用程序的主执行线程分开的后台线程中运行脚本操作。
这样,JavaScript脚本可以创建多个线程,充分利用CPU的多核计算能力,而不会阻塞主线程(通常是UI渲染线程)。
尽管Web Worker是HTML5的一部分,但实际上它在2009年提出了W3C的草案。因此,它具有很好的兼容性,并且在大多数主要的Web浏览器中得到支持。
Web Worker的限制
Web Worker基本上不会破坏JavaScript的单线程性质。
实际上,Web Worker脚本内的代码不能直接操作DOM节点,也不能使用大多数BOM(浏览器对象模型)API。它的全局环境不是Window,而是DedicatedWorkerGlobalScope。Worker在沙箱中运行,执行与主线程完全独立的JavaScript文件。
这些限制是为了避免在前文提到的资源竞争问题中使用Worker。主要用途是作为主线程的辅助,处理高CPU密集型数据处理任务,并通过线程间通信将执行结果返回给主线程。通过这个过程,主线程可以继续响应用户交互,有效地防止了页面延迟的问题。
使用Web Worker
目前,Web Worker在浏览器中得到了广泛支持,只需提供Worker脚本的URI并进行实例化即可使用。
/* main.js */
const worker = new Worker("./worker.js")
通信
Worker和主线程之间的通信只需要两个API,即用于接收消息的onmessage
或addEventListener
以及用于发送消息的postMessage
,可以无缝地实现基于消息的交互。
/* main.js */
const worker = new Worker("./worker.js");
// 从主线程发送消息
worker.postMessage({ data: '从主线程发送的数据' });
// 主线程接收消息
worker.onmessage = (e) => {
const { data } = e;
if (!data) return;
console.log(data);
}
/* worker.js */
// Worker线程接收消息
self.addEventListener('message', (e) => {
const { data } = e;
if (!data) return;
// Worker线程发送消息
self.postMessage({data: 'Worker收到了数据'})
});
注意:在Worker内部,可以使用this.xx
、self.xx
以及直接使用xx
,它们都处于相同的作用域,引用全局变量DedicatedWorkerGlobalScope
,可以互换使用。
终止
有两种方法可以终止Worker。可以在内部终止,也可以从主线程发出终止指令。
/* main.js */
worker.terminate();
/* worker.js */
self.close();