WebWorker简单复习
编辑:浏览器知识出处:本文由 小茗同学 发表
本文demo:http://demo.haoji.me/2018/04/28-webworker/
JS是单线程语言
由于JS设计的初衷就是用来进行一些简单的用户交互以及DOM操作,为了避免复杂性,从一开始JS就被设计成一门单线程语言,现在如是,以后也不会变。
然后正是由于单线程的缘故,当我们需要在前端运行一些大运算量的代码时,浏览器肯定会陷入卡顿。为了解决前端大运算量问题,HTML5引入了WebWorker
。
WebWorker
WebWorker
是浏览器为我们提供的一个可以在浏览器后台开启一个新的线程的API,使得运行在浏览器中的 js 有了多线程的能力。但是这并不意味这js本身就支持多线程,因为这种新线程有很多限制:
- 不能操作DOM;
- 受主线程控制;
- 和普通JS相比有很多限制;
WebWorker
有2种,一种是只能在当前页面使用的Worker
,一种是可以再多个页面之间共享线程的SharedWorker
,前者随着当前页面关闭而关闭,而后者在同域的前提下,可以被多个页面访问。
另外,虽然各类文章里面都把它叫叫WebWorker
,其实它的真正名字就叫Worker
。
2.1. 如何使用
WebWorker
是在主线程中通过传入一个 js 文件的路径来实现的:
index.js:
// new完之后会立即执行
var worker = new Worker('./webworker.js');
webworker.js:
// webworker里面不能访问window,取而代之的是self,而且不能操作DOM
console.log('start');
for(var i=0; i<10000; i++) {
console.log(i);
}
console.log('end');
2.2. self
前面说了,WebWorker
里面不能访问window,取而代之的是self,它们无法访问DOM或者BOM对象,只能调用部分浏览器API,例如:
- XMLHttpRequest
- navigator
- location(只读)
- setTimeout、setInterval等;
- Promise;
- 等等;
当我们执行console.log(self)
时(注意直接控制台执行是没用的,只能在代码里面写):
2.3. 通信
WebWorker
在主线程和子线程之间实现通信的方法有两个:发消息postMessage(data)
和接收消息onmessage(e)
,双方都可以互相发送互相接收。
index.js:
let worker = new Worker ('./webworker.js');
worker.onmessage = (e) => {
console.log(e.data);
}
worker.postMessage('你好,我是主线程!');
webworker.js:
onmessage = (e) => {
console.log(e.data) // main thread got a message
}
// 也可以写成self.postMessage,self可以省略
postMessage('你好,我是子线程!');
2.4. 终止WebWorker
// 在主线程中终止
worker.terminate()
// 在子线程中终止自身
self.close()
2.5. 引入其他脚本
在webworker.js
中还可以引入其它JS文件:
// 引入一个脚本
importScripts('xxx.js');
// 引入多个脚本
importScripts('aaa.js', 'bbb.js', 'ccc.js');
2.6. postMseeage的值传递问题
postMseeage
传递数据的过程其实是一个值拷贝的过程,会现将数据JSON.stringify
之后再JSON.parse
postMseeage
也可以传送二进制数据,但是当数据过大时,由于值拷贝,浏览器会再生成一个该文件的拷贝,这样可能会引起浏览器性能的问题,所以当传输较大数据时,可以直接将数据转移给另一个线程,而不进行值拷贝,只是这样会导致原线程无法再使用这些数据,也能够防止多个线程同时修改的情况发生,这叫做零拷贝,主要是依靠第二个参数:
// 指定传输的所有数据都是零拷贝
let data = new ArrayBuffer(64)
worker.postMessage(data, [data])
// 指定数据中的某个属性零拷贝
let obj = {a: 1, b: 2, c: 3}
worker.postMessage(obj, [obj.a, obj.c])
SharedWorker
关于共享线程我没怎么实测过,主线程中创建一个共享线程:
var sharedWorker = new SharedWorker('./sharedworker.js')
sharedWorker.port.start()
sharedWorker.port.postMessage('你好,我是主线程!');
sharedWorker.port.onmessage = (e) => {
console.log(e.data);
};
子线程 sharedworker.js:
self.onconnect = (e) => {
let port = e.ports[0];
port.addEventListener('message', (e) => {
console.log(e.data); // 特别注意,共享线程的console.log是看不到的
port.postMessage('你好,我是SharedWorker!');
});
port.start();
}
总结
个人觉得,虽然浏览器提供了这么强大的东西,但是99%的场景下都是不需要用到WebWorker的,毕竟现在浏览器性能越来越好,而且在Web上也没有那么多大运算量的场景。关于WebWorker
,简单了解一下就行。
加载全部内容