Web Workers

JAVASCRIPT CONCURRENCY

无响应的脚本

当在HTML 页面中执行脚本时,页面会变得没有响应直到脚本执行完成。

无响应的脚本

什么是Web Worker?

  • web worker是在后台运行的JavaScript,独立于其他脚本,不会影响页面的性能。你可以继续执行你想做的任何事情:点击,选取等等,同时web worker就在后台运行。

  • 浏览器实现Web Workers的方式有很多种,可以使用线程,后台进行,或者运行在其他处理器核心上的进程等。
  • 目前支持Web Workers的浏览器有IE10+, Firefox3.5+, Safari4+, Opera10.6+, Chrome和iOS版的Safari。

HTML5 Web Workers Example

一个简单的示例:创建一个简单的web worker,在后台计数

计数:

Web Worker 特性检测

开始创建一个web worker前,首先要检查浏览器是否支持:

if (typeof(Worker) !== "undefined") {
  // Yes! 支持Web Workers!
  // ...
} else {
  // Sorry! 浏览器不支持Web Workers...
}
			

创建一个Web Worker 文件

因为实例化Worker对象的时候需要传入要执行的JavaScript文件名,所以我们需要在外部创建一个 JavaScript 文件:

var i=0;

function timedCount() {
  i=i+1;
  postMessage(i);
  setTimeout("timedCount()", 500);
}

timedCount();
			

实例化一个Web Worker 对象

下面的代码创建了一个新的web worker 对象,如果指定的JavaScript文件存在的话,浏览器会生成一个新的worker线程,"worker.js"文件文件被异步下载,其中的代码被运行。

var worker = new Worker("worker.js");
			

Worker 和页面之间的通信

Worker是通过message事件和页面通信的,来自Worker的数据保存在event.data中。

在下面的代码中,我们给web worker 对象添加了一个"onmessage"事件监听器。

worker.onmessage = function (event) {
  var data = event.data; 
  // 对数据进行处理
  document.getElementById("result").innerHTML=data;
};
			

在Worker 内部调用postMessage()就可以发送消息到页面。

var i=0;

function timedCount() {
  i=i+1;
  postMessage(i);
  setTimeout("timedCount()", 500);
}

timedCount();
			

使用 JSON 对象传递消息

在所有支持的浏览器中,postMessage()都能支持对象参数,也就是说可以序列化为JSON结构的任何值都可以作为参数传递给postMessage()。

<button onclick="sayHI()">Say HI</button>
<button onclick="unknownCmd()">Send unknown command</button>
<button onclick="stop()">Stop worker</button>
<output id="result"></output>

<script>
  function sayHI() {
    worker.postMessage({'cmd': 'start', 'msg': 'Hi'});
  }
  function stop() {
    // Calling worker.terminate() from this script would also stop the worker.
    worker.postMessage({'cmd': 'stop', 'msg': 'Bye'});
  }
  function unknownCmd() {
    worker.postMessage({'cmd': 'foobard', 'msg': '???'});
  }
  var worker = new Worker('worker.js');
  worker.addEventListener('message', function(e) {
    document.getElementById('result').textContent = e.data;
  }, false);
</script>			
		

使用 JSON 对象传递消息

worker.js代码

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

使用 JSON 对象传递消息

Demo

停止 Web Worker

web worker 对象创建以后,直到被终止掉之前会一直监听有没有消息(即使外部的脚本已经执行完成)。

我们可以使用terminate() 方法来终止一个web worker, 同时释放浏览器和计算机资源。

 // 立即停止worker 的工作
 worker.terminate();
			

在Worker 内部,调用close()方法也可以停止工作。就像在页面中调用terminate()方法一样,Worker停止工作后就不会有事件发生了。

// Web Worker 内部的代码      
self.close();
			

Web Worker 的作用域

  • Web Worker中的代码不能访问DOM,也无法通过任何方式影响页面的外观。
  • Web Worker 中的全局对象是worker 对象本身,this和self引用的都是worker 对象。
  • 当页面在worker 对象上调用postMessage()时,数据会被以异步方式传递给worker,进而触发worker 中的message事件;
  • // 页面 的内部代码,发送数据给worker
    worker.postMessage(data);
    			
  • 同样,为了处理来自页面的数据,在worker 内部也需要创建一个onmessage事件处理程序
  • // Web Worker 的内部代码
    self.onmessage = function(event) {
      var data = event.data;
      //......各种数据处理
      self.postMessage(data); // 把数据再发回给页面
    }
    			

Web Worker 的限制

由于web worker 多线程的特点,web worker 只能访问JavaScript的一些特性:

  • navigator 对象(仅限appName, appVersion, platform, userAgent)
  • location 对象(只读)
  • XMLHttpRequest
  • setTimeout(), setInterval(), clearTimeout()和clearInterval()方法

Worker不能访问:

  • DOM(不是线程安全的)
  • window 对象
  • document 对象
  • parent 对象

Web Worker 错误处理

Worker 内部的 JavaScript 在执行过程中碰到错误时就会触发error事件。

error 事件的三个属性:

  • filename: 发生错误的文件名。
  • lineno: 代码行号。
  • message: 完整的错误信息。
worker.onerror = function(event) {
  console.log("ERROR: " + event.filename + " (" + event.lineno + "): " + event.message);
}
			

Web Worker 的应用场景

凡是比较消耗时间的操作,都可以转交给Worker来做而不会阻塞用户界面

  • 排序
  • 图像处理
  • 加密解密
  • ......

Web Worker 的未来

Web Workers 标准目前在W3C的状态是Candidate Recommendation(2012/05/01),还在继续制定和改进之中。

  • 目前我们讨论的Workers属于"专用 Worker"(dedicated worker)范畴,专门为某个特定页面服务,不能在页面间共享。
  • Web Workers 的另外一个概念是“共享 Worker”(shared worker),可以在浏览器中打开同一个页面间的多个标签之间共享。
  • Worker 内部能访问什么,是不是能够像页面一样访问任何数据?大家还需要关注标准的进展。

扩展阅读

</ Thank you!>