JavaScript语言采用的是单线程模型,也就是说,全部任务排成一个队列,一次只能作一件事。随着电脑计算能力的加强,这一点带来很大的不便,没法充分发挥JavaScript的潜能。龙其考虑到,File API容许JavaScript读取本地文件,就更加如此了。javascript
Web Worker的目就,就是为JavaScript创造多线程环境,容许主线程将一些任务分配给子线程。在主线程运行的同时,子线程在后台运行,二者互不干扰。等到子线程完成计算任务,再把结果返回给主线程。所以,每个子线程就好像一个“工人”(worker),默默地完成本身的工做。html
Web Worker有如下几个特色:html5
使用以前,检查浏览器是否支持这个API。支持的浏览器包括 IE 10+、Firefox 3.6+、Safari 4.0+、Chrome 和 Opera 11,可是手机浏览器还不支持。java
if (window.Worker) { // 支持 } else { // 不支持 }
在主线程内部,采用new命令调用Worker方法,能够新建一个子线程。web
var worker = new Worker('work.js');
Worker方法的参数是一个脚本文件,这个文件就是子线程所要完成的任务,上面代码中是work.js。因为子线程不能读取本地文件系统,因此这个脚本文件必须来自网络端。若是下载没有成功,好比出现404错误,这个子线程就会默默地失败。api
子线程新建以后,并无启动,必需等待主线程调用postMessage方法,即发出信号以后才会启动。数组
worker.postMessage('hello world');
postMessage方法的参数,就是主线程传给子线程的信号。它便可以是一个字符串,也能够是一个对象。浏览器
worker.postMessage({method: 'each', args: ['work']});
在子线程内,必须有一个回调函数,监听message事件。网络
//File: work.js self.addEventListener('message', function(e) { self.postMessage('You said: ' + e.data); }, false);
self表明子线程自身,self.addEventListener表示对子线程的message事件指定回调函数(直接指定onmessage属性的值也能够)。回调函数的参数是一个事件对象,它的data属性包含主线程发来的信号。self.postMessage则表示,子线程向主线程发送一个信号。多线程
根据主线程发来的不一样的信号值,子线程能够调用不一样的方法。
// File: work.js self.addEventListener('message', function(e) { var method = e.data.method; var args = e.data.args;
var reply = doSomething(args); self.postMessage({method: method, reply: reply}); }, false);
主线程也必须指定message事件的回调函数,监听子线程发来的信号。
// File: main.js worker.addEventListener('message', function(e) { console.log(e.data); }, false);
主线程能够监听子线程是否发生错误。若是发生错误,会触发主线程的error事件。
worker.onerror(function(e) { console.log(e); }); // or worker.addEventListener('error', function(e) { console.log(e); }, false);
使用完毕后,为了节省系统资源,咱们必须在主线程调用terminate方法,手动关闭子线程。
worker.terminate();
也能够子线程内部关闭自身。
self.close();
主线程与子线程之间的通讯内容,能够是文本,也能够是对象。须要注意的是,这种通讯是拷贝关系,便是传值而不是传址,子线程对通讯内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通讯内容串行化,而后把串行化后的字符串发给子线程,后者再将它还原。
主线程也子线程这间也能够交换二进制数据,好比File、Blob、ArrayBuffer等对象,也能够在线程之间发送。
可是,用拷贝方式发送二进制数据,会形成性能问题。好比,主线程向子线程发送一个500MB文件,默认状况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript容许主线程把二进制数据直接转移给子线程,可是一旦转移,主线程就没法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫作Transferable Objects。
若是要使用该方法,postMessage方法的最后一个参数必须是一个数组,用来指定前面发送的哪些值能够被转移给子线程。
worker.postMessage(arrayBuffer, [arrayBuffer]);
一般状况下,子线程载入的是一个单独的JavaScript文件,可是也能够载入与主线程在同一个网页的代码。假设网页代码以下:
<!DOCTYPE html> <body> <script id="worker" type="app/worker"> addEventListener('message', function() { postMessage('Im reading Tech.pro'); }, false); </script> </body> </html>
咱们能够读取页面的script,用worker来处理。
var blob = new Blob([document.querySelector('#workere').textContent]);
这里须要把代码看成二进制数据读取,因此使用Blob接口。而后,这个二进制对象转为URL,再经过这个URL建立worker。
var url = window.URL.createObjectURL(blob); var worker = new Worker(url);
部署事件监听代码。
worker.addEventListener('message', function(e) { console.log(e.data); }, false);
最后启动worker。
worker.postMessage('');
整个页面的代码以下:
<!DOCTYPE html> <body> <script id="worker" type="app/worker"> addEventListener('message', function() { postMessage('Work done!'); }, false); </script> <script> (function() { var blob = new Blob([document.querySelector('#worker').textContent]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); worker.addEventListener('message', function(e) { console.log(e.data); }, false); worker.postMessage(''); })(); </script> </body> </html>
能够看到,主线程和子线程的代码都在同一个网页上面。
上面所讲的Web Worker都是专属于某个网页的,当该网页关闭,worker就自动结束。除此以外,还有一种共享式的Web Worker,容许多个浏览器窗口共享同一个worker,只有当全部窗口关闭,它才会结束。这种共享式的Worker用SharedWorker对象来建立,由于适用场合很少,这里就省略了。
[1] Matt West, Using Web Worker to Speed-Up Your JavaScript Applications
[2] Eric Bidelman, The Basics of Web Workers
[3] Eric Bidelman, Transferable Objects: Lightning Fast!
[4] Jesse Cravens, Web Worker Patterns
[5] Bipin Joshi, 7 Things You Need To Know About Web Workers
[6] ruanyf, Web Worker