1-Web Worker 实际操作步骤
Dedicated Worker
接下来我们开始讲解一个使用 Web Worker 运算斐波那契数列的例子。项目地址
首先,我们需要通过Worker()构造器创建worker实例,其只接受一个参数,该参数则是线程脚本的地址,该地址必须符合同源策略。
可以查看代码,我再使用同目录下的 dedicate-worker.js 文件创建了一个 Worker 对象出来,当初始化完成后,浏览器后台线程就回运行该脚本了。
var worker = new Worker('dedicate-worker.js');
那主线程(页面)和后台线程的 Worker 之间是如何通信的呢?这里就要 postMessage 方法了。
worker.postMessage(message, [transfer]);
postMessage 方法接受两个参数,第一个就是 worker 之间传递的数据信息,第二个参数是一个数组,用来转让对象所有权。
/* html 部分
<div class='normal'>
<button onclick='normalPost(34)'>34</button>
<button onclick='normalPost(42)'>42</button>
</div>
*/
// js 部分
function normalPost(num) {
worker.postMessage(num);
}
上述代码中,当我们点击对应的按钮时候,会发送数字到对应的到子线程。后台子进程需要监听 onmessage 事件,才能接受主进程发送来的数据。而该事件接受一个 MessageEvent 对象,其中 data 属性就是主进程传入的数据。
// dedicate-worker.js
// 斐波那契数列
function fabonacci(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}
return fabonacci(n - 1) + fabonacci(n - 2);
}
onmessage = function(messageEvent) {
var str = messageEvent.data;
str = typeof str === 'number' ? String(str) : str;
switch (str) {
// ...
case (str.match(/[0-9]/) || {}).input:
var result = fabonacci(Number(str));
postMessage({msg: str+'的斐波那契数列是:' + result, code: 'fobo'});
break;
default:
postMessage({msg: 'hi, bro~ 慢慢想一下~', code: ''});
break;
}
}
上述代码中,我们通过 onmessage 监听获取主线程传输进来的数据,如果对应的数据运算完成过后,直接调用 postMessage 方法将对应的结果传输出去,注意在子进程的脚本中,因为全局作用域已经改变的原因是可以直接调用 postMessage 的方法,但是在主进程中是不可以的,是需要指定对应的wroker对象调用的。
回到主进程代码,在这里我们也只要让对应的 worker 监听 onmessage 事件即可接受由子线程传递过来的数据。
worker.addEventListener('message', (e) => {
var res = e.data;
switch (e.data.code) {
default:
app.textContent = res.msg;
break;
}
// ...
});
因为 Worker 还是比较消耗资源的,所以当没有用的时候,可以选择关闭,即调用 terminate 方法即可。
var worker = new Worker('dedicate-worker.js');
worker.terminate();
shared Worker
我们实现一个多个 tab 共享部分数据效果。大概这样的效果:

Shared Worker 创建的方法类似于 dedicated Worker,调用 SharedWorker 构造器,语法如下:
new SharedWorker(aURL, name);
其中 aURL 表示脚本的地址,name 表示子线程的名字,同名的线程是可以共享的噢,但依旧要遵循同源策略。在我们的项目中,我们是这样创建的:
var shareWorker = new SharedWorker('share-worker.js', 'sharedWork');
同样的我们可以通过 postMessage 方法将信息传递给子进程,但因为 SharedWorker 实现的原因不同,我们需要通过实例的 port 属性去调用 postMessage。
input.addEventListener('change', (e) => {
shareWorker.port.postMessage({value: e.target.value, type: 'write'});
})
上述代码中,我们通过监听输入框的 change 事件触发主线程传递信息给子线程。而子线程因为作用域的不同,对应的连接主线程方式稍有不同,我们通过监听 onconnect 接通主线程,然后通过接受参数的 ports 属性获取到 MessagePort 对象,这时候便可以使用 onmessage 和 postMessage 处理和传递数据了。
// ...
onconnect = function (messageEvent) {
messageEvent.source.addEventListener('message', (event) => {
swicthByTypeCode(event.data);
})
source.start();
}
// ...
上述代码,主要是子线程连接主线程,并让 MessagePort 对象监听 message 事件,但注意若使用 addEvevntListener 监听的时候,需要自行 start 方法启动。
调试
调试 Dedicated Worker 可以直接在控制台进行调试,唯独调试 SharedWorker 的时候需要进入 chrome://inspect/#workers 点开 inspect 进行调试。


看到了最熟悉的界面,就可以 debugger 调试了。上述仅仅描述了简单的用法,并无涉及某些高级用法,如 importScripts 的应用、错误处理机制等。其实上文有不断的提及到 Worker 的作用域和 window 的作用域是不同的,那接下来我们看看他们到底是什么样子哩?