Web Worker 概述

众所周知,浏览器中的 JavaScript 是单线程的(诸多考量,其一是防止多个线程操作 DOM,造成线程安全问题)。

HTML5 提出的 Web Worker 支持多线程处理功能,因此可以充分利用多核 CPU 带来的优势,将耗时长的任务分配给 Web Worker 运行,从而避免了有时页面反应迟缓,甚至假死的现象。

使用这个 API,用户可以很容易的创建在后台运行的线程,这个线程被称之为 Worker。如果将耗费较长时间的处理交给后台来执行,则对用户在前台页面中执行的操作没有影响。

Web Worker 的特点如下:

  • 通过加载一个 JS 文件来开辟新线程,而不挂起主线程。通过 postMessageonMessage 进行通信。
  • 可以在 Worker 中通过 importScripts(url) 方法来加载 JavaScript 脚本文件。
  • 可以使用 setTimeout()clearTimeout()setInterval()clearInterval() 等方法。
  • 可以使用 XMLHttpRequest 进行异步请求。
  • 可以访问 navigator 的部分属性。
  • 可以使用 JavaScript 核心对象。

除了上述的优点,Web Worker 本身也具有一定局限性的,具体如下:

  • 不能跨域加载 JavaScript
  • Worker 内代码不能访问 BOM(window)和 DOM(document

目前来看,Web Worker 的浏览器兼容性还是很不错的,基本得到了主流浏览器的一致支持。

使用示例

Web Worker 属性和方法:

  • selfthisglobalThis:表示本线程范围内的作用域。
  • postMessage():向创建线程的源窗口发送信息。
  • onmessage:获取接收消息的事件句柄。
  • importScripts(urls):Worker 内部如果要加载其他脚本,可以使用该方法来导入其它 JavaScript 脚本文件。
    • 参数为该脚本文件的 URL 地址,导入的脚本文件必须与使用该线程文件的页面在同一个域中。

例如:

// 在 Worker 中导入 3 个 JavaScript 脚本  
importScripts("a.js", "b.js", "c.js");

Web Worker 的使用方法非常简单,只需要创建一个 Web Worker 对象,并传入希望执行的 JavaScript 文件 URL 地址即可。这里传入的 JavaScript 的 URL 可以是相对或者绝对路径,只要是相同的协议,主机和端口即可。

之后在页面中设置一个事件监听器,用来监听由 Web Worker 对象发来的消息。

如果想要在页面与 Web Worker 之间建立通信,数据需要通过 postMessage() 方法来进行传递。

如果想获取 Worker 进程的返回值,可以通过 onmessage 属性来绑定一个事件处理程序。

Web Worker 不能自行终止,但是能够被启用它们的页面所终止。调用 terminate() 函数可以终止后台进程。被终止的 Web Worker 将不再响应任何消息或者执行任何其他运算。

终止之后,Worker 不能被重新启动,但是可以使用同样的 URL 创建一个新的 Worker。

// 创建 Worker
const worker = new Worker('./worker.js')
 
// 主线程通过监听 message 获取 Worker 传递过来的消息
worker.onmessage = function (event) {
	console.log('来自 Worker 的消息', event.data)
}
 
// 主线程通过 postMessage 给 Worker 传递消息
worker.postMessage({ msg: 'ok 吗?' })

worker.js

// self、this、globalThis 都是全局对象,也可以不写
self.onmessage = function (e) {
  if (e.data?.msg === 'ok 吗?') {
    postMessage({ msg: '木问题!' })
  }
}

WorkerSharedWorker

Web Worker 可分为两种类型:

  • 专用线程 Dedicated Web Worker:window.Worker 类,随当前页面的关闭而结束,这意味着专用线程只能被创建它的页面访问。
  • 共享线程 Shared Web Worker:window.SharedWorker 类,可以同时有多个页面的线程连接

使用 SharedWorker 实现跨标签页通信