JS的多线程 - Web Worker

JavaScript是多线程还是单线程?

JavaScript语言的一大特点就是单线程。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。HTML5提出 Web Worker 标准,允许JavaScript脚本创建多个线程,但是 子线程完全受主线程控制,且不得操作DOM。所以,这个新标准 并没有改变JavaScript单线程的本质

HTML5 - Web Worker

Web Worker 概念

JS提供了一个 Worker 的类,当我们使用这个类的时候,它就会向 浏览器 申请一个新的线程。这个线程就用来 单独执行一个js文件。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。

Web Worker 使用注意点:

  • 同源限制
    分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

  • DOM 限制
    Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用documentwindowparent这些对象。但是,Worker 线程可以使用navigator对象和location对象。

  • 通信联系
    Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过 消息 完成。

  • 脚本限制
    Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

  • 文件限制
    Worker 线程无法读取本地文件,即不能打开本机的文件系统 (file://)它所加载的脚本,必须来自网络

Web Worker 使用

  • 主线程新建worker线程
var worker = new Worker("work.js");
  • 主线程向worker线程 发消息
worker.postMessage(参数);      //参数就是主线程传给 Worker 的数据,可以是文本、对象等等
  • 主线程 接收 来自worker线程的消息
worker.onmessage = function (event) {
  console.log('Received message ' + event.data);
}
  • 主线程 关闭 worker线程
worker.terminate();
  • worker线程 接收 来自主线程的消息
    Worker 线程内部需要有一个监听函数,监听 message 事件。
    其中,self代表子线程自身,即子线程的全局对象
this.addEventListener('message', function (e) {
  self.postMessage('You said: ' + e.data);
}, false)
  • worker线程向主线程 发送 消息
self.postMessage('this is worker');
  • worker线程 关闭 自身
self.close();
  • 错误处理
// 主线程
Worker.onerror:指定 error 事件的监听函数。

//worker线程
self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。

Web Worker 数据通信

主线程给worker线程发送的数据是 文本、对象 的话是拷贝的形式发送。即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。

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

参考资料

猜你喜欢

转载自www.cnblogs.com/baboon/p/12953989.html