Skip to content

WebWorker

1.基本用法

Worker 创建

javascript
// worker.js 文件对应的是所要执行的任务

var worker = new Worker('worker.js');

Worker 通信

​ 主线程与 Worker 之间通信采用的是消息传递机制,具体如下:

主线程

javascript
// postMessage 发送消息
worker.postMessage('Main Hello World');

// 监听消息
worker.onmessage = function (event) {
  console.log('Main Received message ' + event.data);
}

// worker 内部出错时   
worker.onerror = function (error) { }

其他线程

js
// worker 线程
self.postMessage('Worker Hello World');

self.onmessage = function (event) {
  console.log('Worker Received message ' + event.data);
}

self 是一个全局对象,它代表了 Worker 线程自身的环境。在 Worker 文件中,self 取代了在普通浏览器环境中全局可用的 window 对象。

Worker 销毁

worker 执行完任务后,通过销毁可以释放资源。

javascript
// 主线程

worker.terminate()

// worker 线程
self.close()
  • worker.terminate() 是在主线程中调用的方法,用于立即终止 Worker 线程。当你调用 terminate() 时,Worker 线程会被强制停止,它不会有机会完成当前正在执行的任务或进行清理工作。这是主线程对 Worker 线程的一种单向控制。
  • self.close() 是在 Worker 线程内部调用的方法,用于优雅地关闭 Worker 线程。当你从 Worker 线程内部调用 close() 时,Worker 线程会完成当前正在执行的任务,然后关闭自己。这种方式允许 Worker 线程在关闭前进行必要的清理工作。

2.注意事项

​ WebWorker 的使用需要遵循如下一些注意事项:

1、同源策略:分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

2、文件限制:Worker 脚本必须来源于网络文件,不能直接运行内敛 JavaScript 或从本地文件系统加载脚本。

3、作用域隔离:每个 Worker 都有自己的全局执行环境,不能在 Worker 中访问主线程中的JavaScript 对象。

4、结构化克隆:传递给Worker的数据通过结构化克隆算法进行复制。这意味着可以传递大多数JavaScript数据结构,但是不能传递DOM元素或函数。

5、DOM 限制:Worker 中无法读取 DOM 对比,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。

3.Vue 中使用

​ 在 Vue 中使用 Web Worker 略有不同,因为 Webpack、 Vite 等构建工具会对静态资源做处理,对于 Web Worker 而言,需要告知构建工具如何处理 .js 文件的 Worker。

​ 这里以 Vite 为例,分别支持以下两种方式创建 Web Worker。

使用查询后缀导入

语法:

js
import MyWorker from './worker?worker'

const worker = new MyWorker()
vue
<script setup>
import FibWorker from './workers/fib_worker?worker'
const workerMap = {};
function clickHandler(boxId) {
    console.log('boxId', boxId);
    let worker = workerMap[boxId];
    if (!worker) {
        worker = new FibWorker();
        workerMap[boxId] = worker;
        worker.onmessage = (event) => {
            console.log('result', event.data);
            const boxEl = document.getElementById(boxId);
            const inputEl = boxEl.getElementsByTagName('input')[0];
            const number = inputEl.value;
            const spanEl = boxEl.getElementsByTagName('span')[0];
            spanEl.innerHTML = `${number} fib result is ${event.data}`;
        }
    }

    const boxEl = document.getElementById(boxId);
    const inputEl = boxEl.getElementsByTagName('input')[0];
    const number = inputEl.value;
    worker.postMessage(number);
}

</script>

使用构造函数导入

语法:

js
const worker = new Worker(new URL('./worker.js', import.meta.url))
import MyWorker from './worker?worker'
js
const worker = new MyWorker()
vue
<script setup>
const workerMap = {};
function clickHandler(boxId) {
    console.log('boxId', boxId);
    let worker = workerMap[boxId];
    if (!worker) {
        worker = new Worker(new URL('./workers/fib_worker.js', import.meta.url))
        workerMap[boxId] = worker;
        worker.onmessage = (event) => {
            console.log('result', event.data);
            const boxEl = document.getElementById(boxId);
            const inputEl = boxEl.getElementsByTagName('input')[0];
            const number = inputEl.value;
            const spanEl = boxEl.getElementsByTagName('span')[0];
            spanEl.innerHTML = `${number} fib result is ${event.data}`;
        }
    }

    const boxEl = document.getElementById(boxId);
    const inputEl = boxEl.getElementsByTagName('input')[0];
    const number = inputEl.value;
    worker.postMessage(number);
}

</script>

MIT Licensed