HTML5之Web Worker

为什么会有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代码, 通过将消息发布到该代码指定的事件处理程序(反之亦然)。

从上面的解释中,我们总结如下:

  1. 创建Worker时,JS引擎向浏览器申请开一个子线程,这个子线程是浏览器开的。
  2. 2.JS引擎线程与Worker线程间通过特定的方式通信:postMessage。

由于Worker的的特殊性,我们需要明白以下几点限制:

  1. 同源限制。分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
  2. DOM限制。由于子线程和主线程不处于一个上下文,主线程中的一些API在子线程中不可用,如获取DOM、window,以及对DOM的操作。
  3. 通信限制。子线程和主线程不在一个上下文,通信需要通过发送消息完成,即使用postMessage。
  4. 文件限制。为了安全,Worker不能读取本地文件,加载的脚本必须来自网络。
  5. 脚本限制 worker线程不能执行alert、confirm,但可以使用 XMLHttpRequest 对象发出ajax请求。

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.postMessage: 主线程往worker线程发消息,消息可以是任意类型数据,包括二进制数据
  • worker.terminate: 主线程关闭worker线程
  • worker.onmessage: 指定worker线程发消息时的回调,也可以通过worker.addEventListener(‘message’,cb)的方式
  • worker.onerror: 指定worker线程发生错误时的回调,也可worker.addEventListener(‘error’,cb)

Worker线程中的API

  • self.postMessage: worker线程往主线程发消息,消息可以是任意类型数据,包括二进制数据
  • self.close: worker线程关闭自己
  • self.onmessage: 指定主线程发worker线程消息时的回调,也可以self.addEventListener(‘message’,cb)
  • self.onerror: 指定worker线程发生错误时的回调,也可以 self.addEventListener(‘error’,cb)

更多API可参考CDN。

Web Worker场景

  1. 复杂的数学运算,如排序、检索、过滤、分析等。Web Worker最简单的应用就是用来做后台计算,对CPU密集型的场景再适合不过了
  2. 图像处理。如渲染复杂的canvas时需要计算的效果。
  3. 图片预加载。页面加载的图片过多或者图片过大时,或者不能使用懒加载时,可以使用Web Worker加载图片。
  4. 数据预取。如为了提升数据加载性能,可以提前使用Worker线程获取数据,因为Worker线程是可以是用 XMLHttpRequest 的。

使用Web Worker注意事项

  1. 使用Web Worker子线程不会阻塞主线程,但是启动子线程比较耗资源,使用完毕后要关掉。
  2. 大量的XMLHttpRequest请求时,当网速慢时worker中使用XMLHttpRequest和在主线程上使用XMLHttpRequest感受不到阻塞,当网速很快时大量请求返还时会出现卡顿现象。

你可能感兴趣的:(html)