web worker 使用指南
JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。
Web Worker 是HTML5标准的一部分,这一规范定义了一套 API,它允许一段JavaScript程序运行在主线程之外的另外一个线程中。
Web Worker 规范中定义了两类工作线程,分别是专用线程Dedicated Worker和共享线程 Shared Worker,其中,Dedicated Worker只能为一个页面所使用,而Shared Worker则可以被多个页面所共享
特点:
worker线程的创建的是异步的:主线程代码不会阻塞在这里等待worker线程去加载、执行指定的脚本文件,而是会立即向下继续执行后面代码。
postMessage消息交互由内核调度
worker线程数据通讯方式:通信是拷贝关系,即是传值而不是地址,子线程对通信内容的修改,不会影响到主线程。
局限:
同源限制。webworker不能跨域加载JS
DOM限制。worker内代码不能访问dom,原因是worker有自己独立的global worker环境,不是浏览器window,所以alert(),dom等操作无法进行
文件限制。子线程无法读取本地文件,即worker只能加载网络文件。
不是每个浏览器都支持这个新特性,且各个浏览器对Worker的实现不大一致,例如FF里允许worker中创建新的worker,而Chrome中就不行
使用方法
主线程文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23var worker = new Worker('work.js'); //脚本文件即为要执行的任务,不能读取本地文件
//主线程调用worker.postMessage()方法,向 Worker 发消息。
worker.postMessage('Hello World'); //数据可以是各种类型,包括二进制
worker.postMessage({method: 'echo', args: ['Work']});
//监听子线程发回来的消息。
worker.onmessage = function (event) {
console.log('Received message ' + event.data);
doSomething();
}
worker.onerror(function (event) {
console.log([
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
].join(''));
});
function doSomething() {
// 执行任务
worker.postMessage('Work done!');
worker.terminate(); //结束
}
worker线程:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18self.addEventListener('message', function (e) {
var data = e.data;
switch (data.cmd) {
case 'start':
self.postMessage('WORKER STARTED: ' + data.msg);
break;
case 'stop':
self.postMessage('WORKER STOPPED: ' + data.msg);
self.close(); //在 Worker 内部关闭自身。
break;
default:
self.postMessage('Unknown command: ' + data.msg);
};
}, false);
//Worker 内部如果要加载其他脚本,有一个专门的方法importScripts()。
importScripts('script1.js', 'script2.js');
注意点
主线程与 Worker 之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。
主线程与 Worker 之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer 等类型,也可以在线程之间发送。
同页面worker
1 | <!DOCTYPE html> |
上面是一段嵌入网页的脚本,注意必须指定script标签的type属性是一个浏览器不认识的值,上例是app/worker。
1 | var blob = new Blob([document.querySelector('#worker').textContent]); |
上面代码中,先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。