为什么会有Web Worker
我们知道,浏览器中有两个线程:GUI 渲染线程和 JS 引擎线程。 JS线程需要操作DOM,GUI线程渲染界面,两个线程同时运行的话,那么渲染线程前后,页面的数据可能会有不同。因此,在浏览器中GUI 渲染线程和 JS 引擎线程是互斥的,不可同时执行。当JS引擎执行时GUI线程会被阻塞,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。假设JS引擎正在进行巨量的计算,此时就算GUI有更新,也会被保存到队列中,等待JS引擎空闲后执行。由于巨量计算,所以JS引擎很可能很久很久后才能空闲,自然会感觉到巨卡无比。为了在js进行复杂的高耗时计算时,不阻塞页面,那么需要另开一个独立的JS线程去运行高耗时的js代码,这就是HTML5中的Web Worker。
什么是Web Worker
MDN上的解释是
Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外,他们可以使用XMLHttpRequest执行 I/O (尽管responseXML和channel属性总是为空)。一旦创建, 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序(反之亦然)。
从上面的解释中,我们总结如下:
由于Worker的的特殊性,我们需要明白以下几点限制:
Web Worker的基本用法
1.创建Web Worker
在主线程中
var myWorker = new Worker(jsUrl, options)
其中jsUrl为js脚本的的网址,此参数是必须的,是Worker线程要执行的任务。
options是配置对象,是可选的,用以指定Worker的名称。
// 主线程
var myWorker = new Worker('worker.js', { name : 'myWorker' });
// Worker 线程
self.name // myWorker
以上,在主线程中创建了一个子线程,主线程向子线程间的通信如下:
主线程向子线程Worker发送消息需要使用postMessage:
// 主线程
myWorker.postMessage('Hello World');
主线程接收来自子线程Worker发送的消息:
myWorker.onmessage = function (event) {
console.log('Received message ' + event.data); // 子线程发送的数据在事件对象的data中
doSomething();
}
主线程接收完子线程的消息后,如不需要,要关掉:
myWorker.terminate();
2.使用Web Worker
Worker线程中通过添加监听函数,来监听主线程发来的消息。
self.addEventListener('message', function (event) {
doSomething(event.data); // 主线程发送的数据在事件对象的data中
}, false);
其中self表示Worker线程本身,与以下两种写法是一致的。
this.addEventListener('message', function (event) {
doSomething(event.data); // 主线程发送的数据在事件对象的data中
}, false);
addEventListener('message', function (event) {
doSomething(event.data); // 主线程发送的数据在事件对象的data中
}, false);
除了使用addEventListener绑定监听函数一样,也可以使用onmessage绑定监听函数。
与主线程向子线程发送消息一样,子线程向主线程发送消息也是通过postMessage:
self.onmessage = ev => {
postMessage(ev.data + 'hehe~~');
}
Web Worker使用实例
// 子线程thread.js
for (let i = 0; i< 1000; i++) {
setTimeout(() => {
self.postMessage(i)
}, i * 1000);
}
主线程
const worker = new Worker('./thread.js');
worker.onmessage = ev => {
console.log(ev.data);
}
Web Worker API
主线程中的API,其中worker是Worker的实例。
Worker线程中的API
更多API可参考CDN。
Web Worker场景
使用Web Worker注意事项