Web Worker多线程

背景:

  • Js语言采用的是单线程模型
  • 随着计算机能力增强,多核CPU的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力

Web Worker的作用:

  • 为Javascript创造多线程环境,允许主线程创建Worker线程,将一些任务分配给后者运行
  • 主线程运行的同时,Worker线程在后台运行,两者互不干扰
  • 可以降一些计算密集型或高延迟的任务,分给Worker线程负担,主线程(通常负责UI交互)就会很流畅,不会被阻塞或拖慢
  • Worker线程一旦新建成功,就会始终运行,不会被主线程上的活动打断(比如用户点击按钮、提交表单)。这样有利于随时响应主线程的通信。但是,这也造成了Worker比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。

注意点:

  • 同源策略:分配给Worker线程运行的脚本,必须与主线程的脚本文件同源
  • DOM限制:Worker线程所在的全局对象与主线程不一样,无限读取主线程所在的网页的DOM对象,也无法使用documentwindowparent这些对象。但是Worker线程可以使用navigator对象和location对象
  • 通信联系:Worker线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。(postMessage发送消息,onmessage监听消息)
  • 脚本限制:Worker线程不能执行alert()和confirm(),但可以使用XMLHttpRequest对象发出AJAX请求
  • 文件限制:Worker线程无法读取本地文件(file://这类),它所敬爱在的脚本必须来自网络

基本用法:

//主线程
var worker = new Worker(‘./worker.js')
//向Worker发消息
worker.postMessage('hello world')
worker.postMessage({method:'echo', args:['work']})
//监听消息
worker.onmessage = function(event) {
    console.log('Received message' + event.data)
    doSomething()
}

function doSomething() {
 //执行任务
 worker.postMessage('Work done!')
 //结束线程
 worker.terminate()
}
//子线程
//发送消息
this.postMessage('get it!')

//self代表子线程自身,即子线程的全局对象。因此,等同于下面两种写法
self.addEventListener('message', function (e) {
  self.postMessage('You said: ' + e.data);
}, false);

//写法一
//监听消息
this.addEventListener('message', function(event) {
    this.postMessage('You said' + event.data)
}, false)

// 写法二
addEventListener('message', function (e) {
  postMessage('You said: ' + e.data);
}, false);

//根据主线程发来的数据,Worker 线程可以调用不同的方法,下面是一个例子。

self.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(); // Terminates the worker.
      break;
    default:
      self.postMessage('Unknown command: ' + data.msg);
  };
}, false);

Worker加载脚本

//加载单个
importScripts('script1.js')
//加载多个
importScripts('script2.js', 'script3.js')
错误处理
worker.onerror(function (event) {
    console.log([
        'ERROR: Line', e.lineno, 'in ', e.filename, ':', e.message
        ].join('')
    )
})
//或者
worker.addEventListener('error', function(event){
//...
})
关闭Worker
//使用完毕,为了节省系统资源,要及时关闭Worker

//主线程
worker.terminate()
//Worker线程
self.close()

数据通信

  • 主线程与Worker之间的通信内容,可以是文本、对象。这种通信是深拷贝关系,Worker对通信内容修改,不影响到主线程。(浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给Worker,后者再将它还原)
  • 主线程与Worker之间,也可以交换二进制数据,比如File、Blob、ArrayBuffer等

注意点:拷贝方式发送二进制数据会造成性能问题。比如主线程向Worker发送一个500MB的文件,默认情况下浏览器会生成一个源文件的拷贝。
为了解决这个问题,JavaScript允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。
这种转移数据的方法,叫做Transferable Objects。这使主线程可以快速将数据交给Worker,对于影像处理、声音处理、3D运算等就非常方便了,不会产生性能负担。

//直接转移数据的控制权,使用下面的写法:
worker.postMessage(arrayBuffer, [arrayBuffer])
//例如
var ab = new ArrayBuffer(1)
worker.postMessage(ab, [ab])

同页面载入Web Worker

<!DOCTYPE html>
<html>
    <body>
    <!--worker线程-->
    <script id = "worker" type="app/worker">
        addEventListener('message', function(){
            postMessage('some message')
        }, false)
    </script>
    <!--主线程-->
    <script>
        var blob = new Blob([document.querySelector('#worker').textContent])
        var url = window.URL.createObjectURL(blob)
        var worker = new Worker(url)
        worker.onmessage = function(e) {
        //e.tata === 'some message'
        }
    
    </script>
    </body>
</html>

参考链接:http://www.ruanyifeng.com/blog/2018/07/web-worker.html

原文地址:https://www.cnblogs.com/HappyYawen/p/14324226.html