创建WebWorker的四种方式
1. 加载外部脚本文件
😏优点:简单易懂
👿缺点:不易维护
main.js
// 新建 Worker 线程(废话注释~)
var worker = new Worker("worker.js");
// 指定监听函数,接收worker线程发回来的消息。
worker.onmessage = function(e) {
console.log(e.data);
};
// 主线程传给 Worker 的数据。它可以是各种数据类型,包括二进制数据。
worker.postMessage("Test");
worker.js
// self代表子线程自身,即子线程的全局对象
self.onmessage = function(e) {
postMessage(e.data + "!!"); // 向主线程发送消息
};
2. 将自己封装为Worker
将主线程和worker代码写在一个文件中来将自己当作是worker,可以共享函数和类的实现。但无法共享值和实例,因为主线程和worker线程具有不同的上下文。
😏优点:共享功能的实现
👿缺点:不能用作子模块
参考:ouroboros-worker
main.js
const OuroborosWorker = require("ouroboros-worker");
/**
* 主线程
* @param worker
*/
function mainThread(worker) {
window.addEventListener("DOMContentLoaded", function() {
document.getElementById("toggle").onclick = function() {
worker.postMessage({ type: "toggle" });
};
document.getElementById("reset").onclick = function() {
worker.postMessage({ type: "reset" });
};
worker.onmessage = function(e) {
var data = e.data;
if (data.type === "counter") {
document.getElementById("counter").textContent = data.value;
}
};
});
}
/**
* worker线程任务
* @param self
*/
function workerThread(self) {
var counter = 0;
var timerId = 0;
function toggle() {
if (timerId === 0) {
timerId = setInterval(function() {
self.postMessage({ type: "counter", value: counter++ });
}, 250);
} else {
clearInterval(timerId);
timerId = 0;
}
}
function reset() {
counter = 0;
}
self.onmessage = function(e) {
switch (e.data.type) {
case "toggle":
return toggle();
case "reset":
return reset();
}
};
}
OuroborosWorker.run(mainThread, workerThread);
3. 通过字符串创建Worker
将worker线程代码定义为变量,通过URL.createObjectURL(Blob blob)
加载。
乍一看,它看起来很不错,但实际上它上限为30行,并且对编辑器不友好~!但它适合只有一点点worker代码的情况。
😏优点:可以子模块化开发
👿缺点:不方便编辑、不容易阅读、JSLint会报错
参考:How to create a Web Worker from a string
main.js
/**
* 通过字符串创建Worker
* @param workerCode
* @returns {Worker}
*/
const createWorker = (workerCode) => {
// URL.createObjectURL
window.URL = window.URL || window.webkitURL;
let blob;
try {
blob = new Blob([workerCode], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
blob = new BlobBuilder();
blob.append(workerCode);
blob = blob.getBlob();
}
let objectURL = URL.createObjectURL(blob);
return new Worker(objectURL);
};
/**
* Worker 线程所要执行的任务
* @type {string}
*/
const code = `self.onmessage = function (e) {
console.log('Worker Receive: '+e.data);
self.postMessage('Worker: ' + e.data);
}`;
const worker = createWorker(code);
// Test, used in all examples:
worker.onmessage = function (e) {
console.log('Response: ' + e.data);
};
worker.postMessage('Test');
4. 通过函数创建内联Worker
因为这种方法非常酷,我将其称为内联Worker。
利用Function.prototype.toString()
获取函数源代码的字符串,再使用正则
提取函数体
,实现创建Worker。与第三种方式不同的是,该方法易读、易编辑,但是上下文难理解。
😏优点:可以子模块化开发、易读、易编辑
👿缺点:上下文难理解
参考:inline-worker
main.js
/**
* 通过函数创建Worker
* @param workerFun
* @returns {Worker}
*/
const createWorkerFunction = (workerFun) => {
const functionBody = workerFun.toString().trim().match(
/^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/
)[1];
const blob = new Blob([functionBody], {type: "text/javascript"});
const objectURL = URL.createObjectURL(blob);
return new Worker(objectURL);
};
/**
* Worker 线程所要执行的任务
*/
const workerFun = function () {
self.onmessage = function (e) {
console.log('Worker Receive: ' + e.data);
self.postMessage('Worker: ' + e.data);
};
};
const worker = createWorkerFunction(workerFun);
// Test, used in all examples:
worker.onmessage = function (e) {
console.log('Response: ' + e.data);
};
worker.postMessage('Test');