html5 web worker学习笔记(记一)

时间:2021-05-13 19:34:39

  (吐槽:浏览器js终于进入多线程时代!

 

  以前利用setTimeoutsetInterval等方式的多线程,是伪多线程,本质上是一种在单线程中进行队列执行的方式。自从html5 web worker出现,js真正进入了多线程编程时期,现在就开始js的“真·多线程”秘籍修炼吧!

 

最近因为工作中的需要,使用了html5的web worker,之前一直对worker一知半解。直到看到IBM上的一篇博文(知识不是完全有效,可以当作参考),才对worker有了基本概念。

 

worker分类

 worker分为专用线程和共享线程。专用线程只能在当前页面中访问到。而如果要多个页面访问同一个worker,就要使用共享线程,但前提是这几个页面是同域的。

 

worker基本用法

一、专用线程

  1、在浏览器线程中(即插入页面的js代码,内联、外链的js代码都可以):

 1 var worker=new Worker("testWorker.js");
 2 
 3 worker.onmessage=function(event){
 4   /*收到worker线程发送来的数据*/
 5   console.log(event.data);
 6 };
 7 
 8 worker.onerror=function(event){
 9   /*收到worker线程发送来的错误信息*/
10   console.log(event.data);
11 };
12 
13 worker.postMessage("some data");/*向worker线程发送数据*/

      worker一直处于监听状态,要释放这个线程必须在浏览器线程调用worker.terminate();以释放资源。

  2、在worker线程代码中:

1 this.onmessage=function(event){
2       /*收到浏览器线程发来的信息,然后回复浏览器线程*/
3       this.postMessage("worker received data:"+event.data+"("+new Date()+")";
4 };

      在worker线程中,如果不用接收浏览器线程发来数据或者浏览器线程不发送数据,则可以直接执行处理,最后this.postMessage(/*data*/)就行了。

      在上例中,this指线程对象,所以最好的用法是在第一行代码的前面加上“var thread=this;”,然后就可以在任何地方使用thread.postMessage和thread.onmessage方法。worker线程中的代码变成:

1 var thread=this;
2 thread.onmessage=function(event){
3       /*收到浏览器线程发来的信息,然后回复浏览器线程*/
4       thread.postMessage("worker received data:"+event.data+"("+new Date()+")";
5 };

 

二、共享线程

  1、浏览器线程中:

 

 1 var sharedWorker=new SharedWorker("sharedWorker.js");
 2 
 3 worker.onerror=function(e){
 4     /*worker对象错误*/
 5     console.log("create shared worker error.");
 6 };
 7 worker.port.onmessage=function(event){
 8     console.log(event.data);
 9 };
10 worker.port.onerror=function(e){
11      /*worker通信、worker线程中的错误*/
12     console.log(e.message);
13 };
14         
15 worker.port.start();/*必须执行start以开始连接*/

    在浏览器线程中,通信是通过worker的port对象进行的,每一个页面的port是不同的,具有唯一性。最关键的一点是start(),这个函数表示开始连接shared worker,连接成功时,worker线程将增加一个连接数。

    在同域下的其他页面调用此shared worker,代码结构与此相同,只是处理数据的逻辑可能不同。

 

  2、worker线程中:

 1 var thread=this,
 2     connectionCount=0;
 3 thread.onconnect=function(e) {
 4     /*有页面请求连接触发*/
 5     var port=e.ports[0];/*获取指定页面的port,用这个port和页面通信*/
 6     port.postMessage("new connection,index:"+connectionCount);
 7     port.onmessage=function(event) {
 8         port.postMessage("received data:"+event.data);
 9     };
10     connectionCount++;
11 };

    在worker代码中,在onconnect回调函数中获取请求连接者的连接对象port,然后就可以通过这个port与请求者通信。

    在这里,可以把port缓存起来,以后worker可以随时主动postMessage给浏览器特定的页面线程。这需要页面发送自己的“全局唯一特征码”供worker识别。

 

生命周期

  专用线程的生命周期与页面的生命周期一致,可以使用worker.terminate()关闭释放线程。

  共享线程的生命周期与其连接数相关,当连接数为0时,将自动关闭释放。如果需要关闭当前页面的连接,可以调用worker.port.close(),worker线程中的连接数将减少一个。关闭页面也和调用worker.port.close具有同样的影响。

 

尾记

  1、worker也和服务器端的多线程一样,创建、销毁开销较大。所以,worker只应该用于耗时操作,例如复杂、长时间的运算。

  2、worker不能访问DOM及页面相关对象如window document,这也在一定程度上限制了worker的应用,也不能在worker中创建worker。

  3、worker中可以使用XMLHttpRequest,可以自己写一个包装http请求的对象到worker线程代码中,或者找个开源的http库。使用开源库需要解决如何引入的问题,还没有研究。

  4、可以动态创建worker代码。提供思路,具体怎么做的忘了:)       ------------      将需要运行的代码转换为字符串,再转换为二进制的Blob对象,再使用window.URL.createObjectURL创建动态url,参数是Blob对象,然后将这个动态url传入worker的构造函数,作为第一个参数。这种一般是用在专用线程上。例子如下。

 1 var doWorkByHtml5Worker = (function () {
 2         //使用html5的worker进行后台耗时耗Cpu计算
 3         var worker, urlContext = window.URL || window.webkitURL || window.mozURL || window.msURL;
 4         var blobBuilder, blobBuilderContext = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
 5         if (blobBuilderContext) {
 6             var clearBlob = function () {
 7                 blobBuilder = null;
 8                 blobBuilder = new blobBuilderContext();
 9                 blobBuilder.clear = clearBlob;
10             };
11             clearBlob();
12         } else if (Blob) {
13             blobBuilder = {
14                 builder: null,
15                 append: function (str) {
16                     this.builder = new Blob([str]);
17                 },
18                 getBlob: function () {
19                     return this.builder;
20                 },
21                 clear: function () {
22                     this.builder = null;
23                 }
24             }
25         }
26 
27         return {
28             /*
29             * workFunc为需要运行的函数,可以是字符串,也可以是原型函数,如果是函数,将在内部转换为字符串。必须为函数形式。
30             * params为需要传入函数的参数
31             * callback为执行完毕的回调函数,参数为执行workFunc返回的值
32             */
33             Do: function (workFunc, params, callback) {
34                 var workStr = workFunc.toString();
35                 if (workFunc !== workStr) {
36                     workFunc = workStr;
37                 }
38                 blobBuilder.append('postMessage((' + workFunc.toString() + ')(' + JSON.stringify(params) + '));');
39                 worker = new Worker(urlContext.createObjectURL(blobBuilder.getBlob()));
40                 worker.onmessage = function (e) {
41                     this.terminate();
42                     worker = null;
43                     blobBuilder.clear();
44                     callback && callback(e.data);
45                 };
46             }
47         };
48     })();

  使用:

 1 /*基本演示*/
 2 doWorkByHtml5Worker.Do(
 3     function(){
 4         return "data from custom function";
 5     },
 6     null,
 7     function(data){
 8         /*回调*/
 9         console.log(data);/*将打印字符串"data from custom function"*/
10     }
11 );
12 
13 /*带参数演示*/
14 doWorkByHtml5Worker.Do(
15     function(obj){
16         return "received object:{id:"+obj.id+",msg:\""+obj.msg+"\"}";
17     },
18     {id:0,msg:"go home"},
19     function(data){
20         /*回调*/
21         console.log(data);/*将打印字符串"received object:{id:0,msg:"go home"}"*/
22     }
23 );
24 
25 /*传入字符串型执行函数*/
26 var doSomething=(function(){
27     return "data from custom function";
28 }).toString();
29 /* 或者:var doSomething="function(){return \"data from custom function\";}";*/
30 doWorkByHtml5Worker.Do(
31     doSomething,
32     null,
33     function(data){
34         /*回调*/
35         console.log(data);/*将打印字符串"data from custom function"*/
36     }
37 );

 

 

参考:

1、菜鸟教程

2、IBM博文

3、W3C文档