# Web Worker

# 介绍

Web worker 是工作线程的意思,是HTML5中新提出的概念,目前的支持情况如下:Worker support on caniuse (opens new window)

Web Worker的目的,就是为JavaScript创造多线程环境,允许主线程将一些任务分配给子线程。在主线程运行的同时,子线程在后台运行,两者互不干扰。等到子线程完成计算任务,再把结果返回给主线程。因此,每一个子线程就好像一个“工人”(worker),默默地完成自己的工作。这样做的好处是,一些高计算量或高延迟的工作,被worker线程负担了,所以主进程(通常是UI进程)就会很流畅,不会被阻塞或拖慢。

# Worker

Worker之间 以及他们与主程序之间,不会共享任何作用域或者资源,那样子会把多线程编程里面带来的所有问题都带到前端领域

在Worker内部是无法访问主程序的任何资源的。这意味着你不能访问它的DOM环境,也不能访问页面的DOM或者其他资源。这是一个完全独立的线程。

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

你可以做的

  • 网络操作(Ajax)
  • 设定定时器
  • 访问几个常见的全局变量 navigator location JSON
  • 通过importScripts向Worker加载额外的JavaScript脚本 (同步加载)

# 功能

# Web Worker通过消息机制和主线程通讯

eg:

//index.js
(function () {

    var myWorker = new Worker("my_task.js");
    myWorker.onmessage = function (oEvent) {
        console.log("Hi, you are " + oEvent.data.name);
    };
    
    //Send message to sub therad
    myWorker.postMessage({ name: 'Tom' });
})()

这是主线程中的一部分脚本,my_task.js为子线程要执行的脚本,主线程通过监听子线程的message事件被动获取子线程的通知,通过执行postMessage主动向子线程发送消息。同样子线程内也是通过类似方式跟主线程互发消息。

//my_task.js
//可以直接调用,也可以在onmessage函数中调用
postMessage("I am working now");

onmessage = function (oEvent) {
    console.log('Hi, you are ' + oEvent.data.name)
    postMessage({ name: 'Sam' });
};

子线程最基本要实现的方法就是onmessage,这个用于获取主线程的通知。postMessage用于向主线程主动发送通知。

# 值传递

对于Web Worker的参数传递,简单总结就是:Web Worker与主线程之间的参数传递是使用深拷贝的方式。无论被传递的是字符串还是JSON,都是使用深拷贝的方式

# 子线程创建子线程

工作线程也可以创建自己的子线程,并且使用同样的消息机制在父子线程之间交换数据

# 使用的地方

  • 处理密集型数字计算
  • 大数据排序
  • 数据处理(压缩,音频分析,图像处理)
  • 高流量的网络通信

# 使用的限制

  • 同源限制
  • DOM限制
  • 通信限制
  • 脚本限制
  • 文件限制

同源限制 子线程加载的脚本文件,必须与主线程的脚本文件在同一个域

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

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

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

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

# 如何使用

# 新建和启动线程

主线程采用new命令,调用Worker构造函数,可以新建一个子线程

var worker = new Worker('work.js');

Worker构造函数的参数是一个脚本文件,这个文件就是子线程所要完成的任务,上面代码中是work.js

子线程新建之后,并没有启动,必需等待主线程调用postMessage方法,即发出信号之后才会启动。postMessage方法的参数,就是主线程传给子线程的信号。它可以是一个字符串,也可以是一个对象。

worker.postMessage("Hello World");
worker.postMessage({method: 'echo', args: ['Work']});

# 子线程的事件监听

子线程必须有一个回调函数去监听message事件 两种方式:

//1. addEventListener

self.addEventListener('message', function(e) {
  self.postMessage('You said: ' + e.data);
}, false);

self代表子线程自己

//2.onmessage
self.onmessage = function(event) {
  var method = event.data.method;
  var args = event.data.args;

  var reply = doSomething(args);
  self.postMessage({method: method, reply: reply});
};

# 主线程的事件监听

对于主线程和上述的子线程添加事件监听是完全一样的

# 错误处理

主线程可以监听子线程是否发生错误。如果发生错误,会触发主线程的error事件

worker.onerror(function(event) {
  console.log(event);
});

// or

worker.addEventListener('error', function(event) {
  console.log(event);
});

# 关闭子线程(释放资源)

如果不是需要持续执行任务的工作线程,最好在其任务完成后关闭掉。 有两个方法可以完成这个工作:

  • webworker.terminate()这个是在主线程用于关闭子线程的;
  • self.close() 这个是子线程关闭自身的方法。
var worker = new Worker('http://url.com/xxx');

worker.addEventListener('message', function(event){
    //处理数据
    event.data

    worker.terminate(); //在worker的外部关闭worker
})

worker.postMessage('e le me?');

//worker.js
let self = addEventListener('message', function(){

self = postMessage('i am full');
self.close(); //在worker的内部关闭worker
})

# 参考

  1. Web Worker 使用教程 (opens new window)
  2. http://blog.csdn.net/zwjabcd/article/details/50339135
  3. http://javascript.ruanyifeng.com/htmlapi/webworker.html#toc0
陕ICP备20004732号-3