Web Worker 概述
众所周知,浏览器中的 JavaScript 是单线程的(诸多考量,其一是防止多个线程操作 DOM,造成线程安全问题)。
HTML5 提出的 Web Worker 支持多线程处理功能,因此可以充分利用多核 CPU 带来的优势,将耗时长的任务分配给 Web Worker 运行,从而避免了有时页面反应迟缓,甚至假死的现象。
使用这个 API,用户可以很容易的创建在后台运行的线程,这个线程被称之为 Worker。如果将耗费较长时间的处理交给后台来执行,则对用户在前台页面中执行的操作没有影响。
Web Worker 的特点如下:
- 通过加载一个 JS 文件来开辟新线程,而不挂起主线程。通过
postMessage
和onMessage
进行通信。 - 可以在 Worker 中通过
importScripts(url)
方法来加载 JavaScript 脚本文件。 - 可以使用
setTimeout()
、clearTimeout()
、setInterval()
、clearInterval()
等方法。 - 可以使用 XMLHttpRequest 进行异步请求。
- 可以访问
navigator
的部分属性。 - 可以使用
JavaScript
核心对象。
除了上述的优点,Web Worker 本身也具有一定局限性的,具体如下:
- 不能跨域加载 JavaScript
- Worker 内代码不能访问 BOM(
window
)和 DOM(document
)
目前来看,Web Worker 的浏览器兼容性还是很不错的,基本得到了主流浏览器的一致支持。
使用示例
Web Worker 属性和方法:
self
、this
、globalThis
:表示本线程范围内的作用域。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: '木问题!' })
}
}
Worker
和 SharedWorker
Web Worker 可分为两种类型:
- 专用线程 Dedicated Web Worker:
window.Worker
类,随当前页面的关闭而结束,这意味着专用线程只能被创建它的页面访问。 - 共享线程 Shared Web Worker:
window.SharedWorker
类,可以同时有多个页面的线程连接