参考 https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
与 node 的结合使用在 下一篇
相当于浏览器可以执行多线程代码,且不阻塞页面。流程大致:
- 主线程创建 Worker,监听子线程的消息
- 子线程计算完成给主线程发消息
- 结束子进程
但是,
- 不可操作 dom
- 不可操作本地文件
- 线程间消息的传递,是以值复制的方式
demo_workers.js
let i = 0;
setInterval(() => {
i++;
// 向浏览器主线程发消息
postMessage({count: i});
}, 1000);
// 接受主线程的消息
onmessage = (e) => {
i = e.data;
}
index.html
<html>
<head>
<title>Web Workers Demo</title>
</head>
<body>
<div id="count"></div>
<script type="text/javascript">
if (typeof Worker !== "undefined") {
// 创建一个新的 web worker 对象,然后运行 "demo_workers.js" 中的代码
let w = new Worker("http://localhost:8080/static/demo_worker.js");
// 监听 web worker 发回的消息
w.onmessage = (event) => {
// 发回的消息 event 可以是一个对象
document.getElementById("count").innerHTML = event.data.count;
if (event.data.count == 10) {
// 向 web worker 发消息
w.postMessage(90);
}
if (event.data.count == 100) {
// 终止 Web Worker(不终止不知道会不会内存泄露,或一直监听浪费资源)
w.terminate();
document.getElementById("count").innerHTML +=
"WebWorkers运行已终止";
}
};
// 错误监听
w.onerror = function (error) {
console.error(
"Worker error: " +
error.message +
",file: " +
error.filename +
", line: " +
error.lineno +
"\n"
);
throw error;
};
} else {
document.getElementById("count").innerHTML =
"你的浏览器不支付WebWorkers";
}
</script>
</body>
</html>
语法
主进程:
// 创建对象
var worker = new Worker("/static/download.js");
// 发送消息(给worker)
worker.postMessage({ cmd: "download", params: { qingguo_bookid: "" } });
// 监听(worker的)消息
worker.onmessage = function (event) {
console.log("Received message " + event.data);
// 主进程关闭worker
// worker.terminate();
};
// 监听(worker的)错误
worker.onerror = function (event) {
console.log(
["ERROR: Line ", e.lineno, " in ", e.filename, ": ", e.message].join("")
);
};
worker:
// 监听主进程消息
// self => worker的this对象
self.addEventListener(
"message",
function (e) {
// 发送消息(给主进程)
self.postMessage("You said: " + e);
// worker关闭自己
self.close();
},
false
);
生成 subworker
如果需要的话 worker 能够生成更多的 worker。这就是所谓的 subworker,它们必须托管在同源的父页面内。而且,subworker 解析 URI 时会相对于父 worker 的地址而不是自身页面的地址。这使得 worker 更容易记录它们之间的依赖关系。
共享 worker
一个共享 worker 可以被多个脚本使用——即使这些脚本正在被不同的 window、iframe 或者 worker 访问
vue 中使用 webworker,基于 worker-loader
中文 API: https://doc.codingdict.com/webpack-cn-doc/loaders/worker-loader/
cnpm i -D worker-loader
chainWebpack: config => {
config.module
.rule('worker-loader')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end()
}
import WebWorker from './my.worker.js';
if (Worker) {
const worker = new ReqWebWorker();
worker.onerror = ...
worker.onmessage = ...
worker.postMessage(...);
注意:
- WebWorker 脚本文件必须与父页面相源;
- WebWorker 加载耗资源、耗时(初始化线程、事件队列等),具有不可忽略的启动延迟,日志打印常在主线程之后,在通过日志确定操作顺序时须当心;
self.close()
不是同步的;worker.terminate()
是同步的;self.importScripts()
没有 CORS 限制,类似于<script>
引入postMessage
传递的数据:结构化克隆算法(structured clone algorithm)、可转移对象(transferable objects)和共享数组缓冲区(shared array buffers)。
- 克隆 Error 对象、Function 对象或 DOM 节点会抛出错误。
- 可转移对象有:ArrayBuffer\MessagePort\ImageBitmap\OffscreenCanvas,ArrayBuffer 的内存从父上下文转移到了工作者线程
- SharedArrayBuffer 兼容性有问题
navigator.hardwareConcurrency
属性返回的系统可用的核心数量,可做为线程池大小的依据