Web Worker 使用

简介

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

等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢

使用限制

  1. 同源限制
    分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源
  2. DOM 限制
    Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用 document、window、parent 这些对象。但是,Worker 线程可以 navigator 对象和 location 对象。
  3. 通信联系
    Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成
  4. 脚本限制
    不能执行 alert()方法和 confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求
  5. 文件限制
    Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络. 【本地开发使用 localhost】

使用 API

结构:

1
2
3
4
5
├── index.html
├── worker-helper
│ ├── 1.js
│ └── 2.js
└── worker.js

示例:

worker 中的全局对象是 DedicatedWorkerGlobalScope,可以当作是 不具有 DOM 对象的 Window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="file" />
<script>
const worker = new Worker("./worker.js", { name: "myWorker" });

// 监听worker进程发送的消息
worker.onmessage = function (event) {
console.log("Received message & Work done!" + event.data);

// 关闭worker线程
if (event.data === "close") {
worker.terminate();
}
};
// 监听错误事件
worker.onerror = function (event) {
console.log(`ERROR: Line ${e.lineno} in ${e.filename} : ${e.message}`);
};
// worker.postMessage 的参数可以传给 Worker 各种数据类型,包括二进制数据
worker.postMessage("Hello World");
worker.postMessage({ method: "echo", args: ["Work"] });

// setTimeout(()=> {
// worker.postMessage('close')
// }, 3000)

console.log(worker);

const ab = new ArrayBuffer(100);
// postMessage 的 第二个参数是 要转移到 worker 线程的变量
// 被转移的 变量类型是 ArrayBuffer、MessagePort 或 ImageBitmap

worker.postMessage(ab, [ab]); // 将变量转移, 第一参数不可以是别的,因为 worker中只能接收第一个参数

console.log(ab); // 此时是 ArrayBuffer(0) 代表已从主线程中去除

worker.addEventListener("message", function (event) {
if (event.data.byteLength) {
console.log(event.data);
}
});
</script>

<!-- 也可以以标签的形式写web worker -->
<script id="worker" type="app/worker">
addEventListener('message', function () {
postMessage('some message');
}, false);
</script>
<script>
// 然后再读取。。。
const blob = new Blob([document.querySelector("#worker").textContent]);
const url = window.URL.createObjectURL(blob);
const scriptWorker = new Worker(url);

scriptWorker.onmessage = function (e) {
// e.data === 'some message'
};
</script>
</body>
</html>

代码中的 “转移底层” 的概念,也就是 postMessage 的 第二个参数,更多请参考 MDN 文档.

SharedArrayBuffer 类型不能被转移,因为已经被共享,SharedArrayBuffer 允许 Worker 线程与主线程共享同一块内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// worker.js
console.log(this); // DedicatedWorkerGlobalScope {}
console.log(`worker name: ${this.name}`); // myWorker
addEventListener(
"message",
function (e) {
console.log(e);
postMessage(`You said: ${e.data}`);

// 自身关闭 worker 线程
if (e.data === "close") {
close();
}

if (e.data.byteLength) {
// 重新移交给主线程
postMessage(e.data, [e.data]);
console.log("worker buffer", e.data); // 此时的 e.data 是 ArrayBuffer(0)
}
},
false
);

// 加载其他脚本
importScripts("./worker-helper/1.js", "./worker-helper/2.js");

// 监听错误事件
addEventListener("error", function (e) {});

// worker 中也可以再创建 worker
// const newWorker = new Worker('./worker-helper/1.js')
作者

Huasun47

发布于

2022-07-23

更新于

2022-07-23

许可协议