Web Worker

Web Worker

Jul 27, 2022 ·
5 Min Read

一、概述

众所周知,JavaScript 语言采用的是单线程模型,也就是,所有任务在一个线程上完成,一次只能做一件事。如果前面的任务没有完成,则阻塞后面的任务。极大浪费 CPU 的计算能力↘️。

避免阻塞有两种方法

  1. 异步(这里不展开说)
  2. 多线程(其实也不算是避免阻塞,只是同时可以处理多个任务,减少阻塞的可能)

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker线程,将一些任务分配给子线程运行,主线程和子线程互不干扰。

场景:比如在一些需要密集计算的任务,可以交给 子线程 执行,主线程继续负责 UI 交互,保证页面的流畅性。

注意

  1. 同源限制,即 Worker 线程的脚本文件必须与主线程 的脚本文件同源。
  2. 无法访问 DOM, Worker 线程 无法读取主线程所在网页的 DOM 对象,也就是没有 documentwindowparent,但有 navigator 对象和 location 对象。
  3. 通信限制,必须通过 postMessage() 和 监听 onmessage() 来进行通信。
  4. 不能访问本地文件,即 file://

二、基本使用

📖 Web Worker MDN 文档

(1)创建 Worker 线程

通过调用 Worker() 构造函数,新建一个 Worker 线程

// 第一个参数必须是一个脚本文件,第二个参数可选,详情可看文档
const worker = new Worker('myWork.js')

(2)向 Worker 线程发送信息

// postMessage 可以发送任意类型的数据
worker.postMessage('hello world!')
worker.postMessage({
eventType: 'sayHi',
message: 'hellp world!'
})

(3)主线程接收 Worker 线程信息

worker.onmessage = (event) => {
// 通过事件对象的 data 属性获取 Worker 发送过来的信息
console.log('worker message:', event.data)
// do something
}

(4)销毁 Worker 线程

// 建议 Worker 完成任务后,关闭 Worker 线程,避免浪费资源。
// 注意:销毁或 Worker 线程主动关闭后,必须需要重新创建使用。
worker.terminate()

(5)Worker 线程接收主线程信息 和 向主线程发送信息

// 三种写法等价的,self 和 this 都代表子线程的全局对象,可以使用 addEventListener,也可以使用 onmessage。
// Worker 线程也和主线程一样,使用 possMessage() 发送信息。
// 写法一
addEventListener('message', e => {
// do something
postMessage('I am a Worker thread')
}, false)
// 写法二
this.addEventListener('message', e => {
// do something
this.postMessage('I am a Worker thread')
}, false)
// 写法三
self.addEventListener('message', e => {
// do something
self.postMessage('I am a Worker thread')
}, false)

(6)关闭自身 Worker 线程

// 在 Worker 线程使用。
// 效果与 主线程调用 terminate() 一样,只是一个由主线程发起销毁 Worker 线程,而这里是 Worker 线程主动关闭。
self.close()

(7)Worker 加载脚本

importScripts('spript1.js'[, ...])
// 除此之外配合 webpack 使用,则可以使用 ES6 import 加载方法。

主要方法

  • Worker.onerror:指定 error 事件的监听函数。
  • Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
  • Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
  • Worker.postMessage():向 Worker 线程发送消息。
  • Worker.terminate():立即终止 Worker 线程。

三、webpack 或 vue 中使用

webpack 4.x 版本下 需要安装 work-loader

详细配置建议查看 work-loader github 的 README,webpack 官网下 loader 文档稍微有点旧了,新版的 work-loader 部分配置属性名更改或删除。

💡

webpack.config.js
// webpack
{
module: {
rules: [
{
test: /\.worker\.js$/,
use: { loader: 'worker-loader' }
options: {
inline: 'fallback',
filename: '[name].[hash].js'
}
}
]
}
}
// vue cli
// vue.config.js
chainWebpack: (config) => {
config.module
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.options({
inline: 'fallback',
filename: '[name].[hash].js'
}).end()
config.output.globalObject('this') // 注入全局变量
config.module.rule('js').exclude.add(/\.worker\.js$/) // 修改响应
}

💡使用

// where you need it
import worker from 'myWorker.js'
const worker = new worker()
worker.onmessage = function (event) {
// Received message:I am the message from the thread
console.log('Received message:' + event.data)
worker.terminate()
}
worker.postMessage('hello world!')
// myWorker.js
addEventListener('message', function(e) {
// from main thread:hello world!
console.log('from main thread:' + e.data)
// do something
self.postMessage('I am the message from the thread')
}, false)

❗ 在 webpack 5.x 中,可以使用 web workers 来代替 work-loader

const worker = new Worker(new URL('./myWorker.js', import.meta.url))
// do something
Last edited Feb 15