web worker小记
前瞻
JS是单线程的,这就意味着我们在一个标签上就浪费了多核能力。 JS与UI共用同一个线程,我们在使用JS来运行CPU密集型任务 或者是 阻塞JS时,UI线程也会阻塞。这是我们不想看到的。
web worker的作用就是弥补这个缺陷而产生的,为JS提供了多线程能力。
他可以让主线程去创建worker线程,然后分配一些任务给它运行。
主线程在运行的时候,worker线程也在后台运行,两者是互不干扰的。但是worker线程一旦创建,就会一直运行,随时等待主线程传输信息,这也就意味着它是比较消耗资源的(以内一直运行),所以不应该过度使用,使用完毕后应当及时关闭。
简单使用
- worker的脚本文件必须与主JS脚本文件同源。
- 无法读取Dom,Bom可以。
- 不在同一个上下文环境,需要通过消息API传输信息。
- alert,confirm不被允许,但可以使用ajax。
- 无法读取本地文件,即不能打开本机的文件系统(file://)
// 使用Worker构造函数构造worker实例
// 参数为脚本文件(必须按照同源政策请求网络的Js文件)
const worker = new Worker('work.js')
// 主线程 传输信息/监听信息(可以使任何类型的数据)
worker.postMessage('hello, bbd');
worker.onmessage = (e) => {
console.log(e.data)
}
// worker子线程只需要使用Dom3监听相应事件
// 以下使用this,但使用self可能更加直观且不易出错。
this.addEventListener('messafe', (e) => {
this.postMessage('sss')
})
也可以直接 this.onmessage
wroker内如何加载脚本
// 可以加载多个脚本
importScripts('script1.js');
错误处理
// 如果worker发生错误就会触发
worker.onerror(function (event) {
console.log(event)
});
// 或者
worker.addEventListener('error', function (event) {
// ...
});
关闭worker
// 主线程
worker.terminate()
// worker自身
self.close()
数据通信
前面提到,信息传输可以是任何数据。需要注意的是这种传输的关系是拷贝。是传值非传址。
我们也可以传输文件给worker线程,但是我们提到,这种传值是通过拷贝关系的,不可能我们在前端计算10G大小的文件的时候,把10G都拷贝到worker上去。我们可以吧数据直接转给worker线程,主线程不在享有文件数据。也防止了多个线程同时修改数据的麻烦。
// Transferable Objects 格式
worker.postMessage(arrayBuffer, [arrayBuffer]);
// 例子
var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
同页面使用(一般不使用)
一般来说worker都会单独载入一个JS文件,但是也可以载入与主线程在同一个html页面的代码
// 这里需要注意,必须指定一个html无法识别的type
<!DOCTYPE html>
<body>
<script id="worker" type="app/worker">
addEventListener('message', function () {
postMessage('some message');
}, false);
</script>
</body>
</html>
// 获取上面所写的js内容,转变为文本数据
var blob = new Blob([document.querySelector('#worker').textContent]);
// 然后使用它创建一个生产url
// worker再加载url完成创建
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
worker.onmessage = function (e) {
// e.data === 'some message'
};
实战中可以使用到,例如轮询问,计算hash值等等。