參考 https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
與 node 的結(jié)合使用在 下一篇
相當(dāng)于瀏覽器可以執(zhí)行多線程代碼,且不阻塞頁(yè)面。流程大致:
- 主線程創(chuàng)建 Worker,監(jiān)聽(tīng)子線程的消息
- 子線程計(jì)算完成給主線程發(fā)消息
- 結(jié)束子進(jìn)程
但是,
- 不可操作 dom
- 不可操作本地文件
- 線程間消息的傳遞,是以值復(fù)制的方式
demo_workers.js
let i = 0;
setInterval(() => {
i++;
// 向?yàn)g覽器主線程發(fā)消息
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") {
// 創(chuàng)建一個(gè)新的 web worker 對(duì)象,然后運(yùn)行 "demo_workers.js" 中的代碼
let w = new Worker("http://localhost:8080/static/demo_worker.js");
// 監(jiān)聽(tīng) web worker 發(fā)回的消息
w.onmessage = (event) => {
// 發(fā)回的消息 event 可以是一個(gè)對(duì)象
document.getElementById("count").innerHTML = event.data.count;
if (event.data.count == 10) {
// 向 web worker 發(fā)消息
w.postMessage(90);
}
if (event.data.count == 100) {
// 終止 Web Worker(不終止不知道會(huì)不會(huì)內(nèi)存泄露,或一直監(jiān)聽(tīng)浪費(fèi)資源)
w.terminate();
document.getElementById("count").innerHTML +=
"WebWorkers運(yùn)行已終止";
}
};
// 錯(cuò)誤監(jiān)聽(tīng)
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>
語(yǔ)法
主進(jìn)程:
// 創(chuàng)建對(duì)象
var worker = new Worker("/static/download.js");
// 發(fā)送消息(給worker)
worker.postMessage({ cmd: "download", params: { qingguo_bookid: "" } });
// 監(jiān)聽(tīng)(worker的)消息
worker.onmessage = function (event) {
console.log("Received message " + event.data);
// 主進(jìn)程關(guān)閉worker
// worker.terminate();
};
// 監(jiān)聽(tīng)(worker的)錯(cuò)誤
worker.onerror = function (event) {
console.log(
["ERROR: Line ", e.lineno, " in ", e.filename, ": ", e.message].join("")
);
};
worker:
// 監(jiān)聽(tīng)主進(jìn)程消息
// self => worker的this對(duì)象
self.addEventListener(
"message",
function (e) {
// 發(fā)送消息(給主進(jìn)程)
self.postMessage("You said: " + e);
// worker關(guān)閉自己
self.close();
},
false
);
生成 subworker
如果需要的話 worker 能夠生成更多的 worker。這就是所謂的 subworker,它們必須托管在同源的父頁(yè)面內(nèi)。而且,subworker 解析 URI 時(shí)會(huì)相對(duì)于父 worker 的地址而不是自身頁(yè)面的地址。這使得 worker 更容易記錄它們之間的依賴關(guān)系。
共享 worker
一個(gè)共享 worker 可以被多個(gè)腳本使用——即使這些腳本正在被不同的 window、iframe 或者 worker 訪問(wèn)
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 腳本文件必須與父頁(yè)面相源;
- WebWorker 加載耗資源、耗時(shí)(初始化線程、事件隊(duì)列等),具有不可忽略的啟動(dòng)延遲,日志打印常在主線程之后,在通過(guò)日志確定操作順序時(shí)須當(dāng)心;
self.close()不是同步的;worker.terminate()是同步的;self.importScripts()沒(méi)有 CORS 限制,類似于<script>引入postMessage傳遞的數(shù)據(jù):結(jié)構(gòu)化克隆算法(structured clone algorithm)、可轉(zhuǎn)移對(duì)象(transferable objects)和共享數(shù)組緩沖區(qū)(shared array buffers)。
- 克隆 Error 對(duì)象、Function 對(duì)象或 DOM 節(jié)點(diǎn)會(huì)拋出錯(cuò)誤。
- 可轉(zhuǎn)移對(duì)象有:ArrayBuffer\MessagePort\ImageBitmap\OffscreenCanvas,ArrayBuffer 的內(nèi)存從父上下文轉(zhuǎn)移到了工作者線程
- SharedArrayBuffer 兼容性有問(wèn)題
navigator.hardwareConcurrency屬性返回的系統(tǒng)可用的核心數(shù)量,可做為線程池大小的依據(jù)