HTML5的时代来到了,由于前端负责的数据处理越来越大,单单靠一个JS的单线程越来越力不从心,webWorker的出现能更好的解决该问题,这篇文章将讲述webWorker的使用。
简单的实例 使用webWorker我们可以将一些大量计算的事情放置在另一个线程进行处理,这时候我们页面不会被卡死,让网页使用流畅。
比如我们进行一个50亿次的加法计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 console .time ('启动页面' );function  add5billion ( ){    var  x=0 ;     for (var  i=0 ;i<1000000000 ;i++){         x++;         x++;         x++;         x++;         x++;     }     return  x; } add5billion ();console .timeEnd ('启动页面' );
 
 
在我的电脑上是2秒多,但一般的电脑可能要慢很多,但是作为页面展示如果需要2秒的时间那么用户体验将是相当不好的,因为两秒种的时间用户都无法操作。
这时候一般只能使用 setTimeout 将计算拆除,或者请求服务器处理,但是都很麻烦,在这里我们使用 Worker 就可以了。
script of html
1 2 3 4 5 6 7 8 9 10 11 console .time ('启动页面' );var  worker=new  Worker ('worker-5x10^9.js' );worker.onmessage =function (e ){     console .log (e.data ); } console .timeEnd ('启动页面' );
 
worker-5x10^9.js
1 2 3 4 5 6 7 8 9 10 11 var  x=0 ;for (var  i=0 ;i<1000000000 ;i++){    x++;     x++;     x++;     x++;     x++; } this .postMessage (x);
 
 
 
使用webWorker可以避免大量的计算导致的拥塞,避免页面假死,充分利用客户端的物理资源。
下面我们来详细讲述如何使用 webWorker。
Worker简介 兼容性 虽然作为HTM5的新特性,但是各大浏览器对 webWorker 的支持都是很好的,所以我们基本可以大胆的尝试在工程中使用。
 
构造函数 webWorker 在浏览器中的构造函数为 Worker()
但是必须传入一个字符串参数作为 webWorker 运行的内容,在上面的示例中便是传入了 worker-5x10^9.js 来让其执行。
语法 
  new Worker(aURL);
 
参数 
参数名 
参数类型 
参数描述 
是否必要 
 
 
aURL 
String 
webWorker运行脚本的地址,它必须遵循同源策略 
TRUE 
 
用法 1 2 3 var  worker = new  Worker (workerURL);
 
实例API 我们可以看看输出worker实例对象。
 
我们可以看到它主要包含4个方法:
 onerror 
 onmessage 
 postMessage 
 terminate 
 
十分简单
onerror 这个方法是在Worker的error事件触发时执行,需要给onerror赋值一个函数,并且会给该函数传递一个事件对象。
script of html
1 2 3 4 5 6 7 let  worker = new  Worker ('worker-error.js' );worker.onerror =function (e ){     console .log ('错误对象' ,e); } 
 
worker-error.js
 
 
这样我们就可以获取到内部的错误信息,并加以处理,但是一般对这方面的需求很少,主要获取内部运行的情况。
onmessage 这个方法是在Worker的内部执行 postMessage 事件触发时执行,需要给onmessage赋值一个函数,并且会给该函数传递一个事件对象。传递的值通过事件对象的 data 属性获取。
script of html
1 2 3 4 5 6 7 let  worker = new  Worker ('worker-postMessage.js' );worker.onmessage =function (e ){     console .log (e,e.data ); } 
 
worker-postMessage.js
1 2 3 postMessage ("I'm Worker" );
 
 
这样我们就可以获取到内部的发出信息。
postMessage(aMessage, transferList) 这个方法是向Worker的内部发送消息,在这里不止是传递字符串,可以传递对象等,但是不能传递函数包含的对象。
他会对对象进行深度克隆,所以可以用来进行对象的异步的深度克隆。这一部分在下面进行详细阐述。
参数名 
参数类型 
参数描述 
是否必要 
 
 
aMessage 
any 
需要发送的消息,可以传递对象,会对对象(可以包含js的内置对象类型)进行深度克隆,但是不支持函数的传递 
FALSE 
 
transferList 
array 
将对象的上下文环境移交给worker 
FALSE 
 
script of html
1 2 3 4 5 6 7 8 9 10 11 let  worker = new  Worker ('worker-backData.js' );worker.onmessage =function (e ){     console .log (e.data ); } worker.postMessage ('123' ); worker.postMessage ({x :1 }); worker.postMessage ({x :1 ,y :function ( ){}}); 
 
worker-postMessage.js
1 2 3 4 5 onmessage=function (e ){     postMessage (JSON .stringify (e.data )); } 
 
 
由于执行是异步的,所以还是要主线程的JS执行完毕后才能执行对事件的响应,第一个错误是因为对有函数的对象解析导致的,所以我们只能看到前面两个的字付串输出。
terminate() 这个函数是立刻停止worker的运行,不会等待worker的运行完成。
script of html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let  worker = new  Worker ('worker-backData.js' );worker.onmessage =function (e ){     console .log (e.data ); } worker.postMessage ('123' ); setTimeout (()=> {    worker.terminate ();     worker.postMessage ('223' ); },1000 ) 
 
 
我们可以看到这里只有 123 输出,虽然依然可以向worker发送消息,但是没什么卵用。
webWorker内部环境 worker的执行的环境和浏览器环境的有一些不同,浏览器内全局变量是 window 而 worker内部是DedicatedWorkerGlobalScope 
在这个环境中无法使用window来获取全局变量
script of html
1 2 3 4 5 6 7 let  worker = new  Worker ('worker-window.js' );worker.onmessage =function (e ){     console .log (e.data ); } 
 
worker-window.js
1 2 3 window .postMessage ('123' );
 
 
我们可以看到window是没有定义的,想要获取我们需要使用 self
worker-self.js
1 2 3 self.postMessage ('123' ); 
 
 
这样我们就可以获取内部的全局变量了,其实self在浏览器环境下也是指向全局环境的。
全局环境 worker的内部环境当然也和浏览器下的环境差不多,都含有相关的全局方法。
console worker的内部环境也是支持console.log来方便调试的,并且它的输出会展示在浏览器的控制台当中
worker-self.js
1 2 3 4 5 console .log ('console.log' )console .log ('console.error' )console .info ('console.info' )
 
 
对AJAX和webSocket的支持 1 2 3 4 console .log (XMLHttpRequest )console .log (WebSocket )
 
这两个对象都是支持的,所以webWorker,可以负责数据请求的收发。
无法操作DOM  
worker内部是无法操作DOM的,主要是为了线程安全,但是我们在采用VDOM的时候,便可以放入worker内部处理VDOM。
postMessage 在worker的postMessage方法,已经worker内部的postMessage都是支持对象传递的,它可以传递满足The structured clone algorithm | 结构化克隆算法 的对象。
script of html
1 2 3 4 5 6 7 8 9 let  worker = new  Worker ('worker-transferList.js' );let  a={x :1 };let  obj={x :a,y :a,z :{x :1 }}worker.postMessage (obj); 
 
worker-transferList.js
1 2 3 4 5 6 onmessage=function (e ){     console .log (e.data .x ===e.data .y );     console .log (e.data .x ===e.data .z ); }; 
 
同时在深复制的时候还保持了内部的索引结构。
transferList 最难弄清的其实是 postMessage 的 transferList 参数。
transferList数组类型 transferList数组类型必须为 ArrayBuffer, MessagePort and ImageBitmap
1 2 3 4 5 6 7 8 9 10 11 let  worker = new  Worker ('worker-transferList.js' );let  obj={x :1 }worker.onmessage =function (e ){     console .log (e.data ); } worker.postMessage (obj,[obj]); 
 
我们设置成 ArrayBuffer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let  worker = new  Worker ('worker-transferList.js' );let  bufArr=new  ArrayBuffer (100 );let  obj={x :bufArr}console .log (bufArr.byteLength );worker.onmessage =function (e ){     console .log (e.data ); } worker.postMessage (obj,[bufArr]); console .log (bufArr.byteLength );
 
我们可以看到post成功了,但同时我们会发现bufArr.byteLength的长度改变了,因为byteLength的上下文已经搬移到worker内部了。
script of html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let  worker = new  Worker ('worker-transferList.js' );let  bufArr=new  ArrayBuffer (100 );let  obj={x :bufArr}console .log (bufArr.byteLength );worker.onmessage =function (e ){     console .log (e.data ); } worker.postMessage (obj,[bufArr]); console .log (bufArr.byteLength );
 
worker-transferList.js
1 2 3 4 5 6 7 onmessage=function (e ){     console .log ('worker beforeSend' ,e.data .x .byteLength );     postMessage (e.data ,[e.data .x ]);     console .log ('worker afterSend' ,e.data .x .byteLength ); }; 
 
 
Worker 和 SharedWorker 上述讲述的都是页面独享 Worker 也叫 DedicatedWorker ,然而还有一个 SharedWorker。
DedicatedWorker 会在页面关闭时随之关闭,而 SharedWorker 会在所有关联的页面都关闭后才关闭。
shared.js
1 2 3 4 5 6 7 8 9 10 11 let  worker = new  SharedWorker ('worker-shared.js' );worker.port .start (); worker.port .postMessage ('123' ); worker.port .onmessage =function (e ){     console .log (e.data ); }; 
 
worker-shared.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var  x=0 ;function  add ( ){    setTimeout (()=> {         x++;         add ();     },100 ); } add ();addEventListener ("connect" , function (e ){    var  port = e.ports [0 ];     port.addEventListener ('message' , function (e ) {       port.postMessage (x);     });     port.start ();  }); 
 
当我们只在第一个页面进行刷新时,输出的都是0,但当有两个页面时,刷新一个页面输出的内容就不再只是0。
当只有一个页面时
 
当有两个页面时
 
注意 
我也不知道为何,直接写在HTML中的sharedWorker并不会执行。 
 
总结 这里 webWorker 简单的介绍就算完了,使用 webWorker 能减少页面的拥塞,充分利用客户的物理资源处理大量数据同时还可以用于封装接口层,毕竟是支持AJAX和webSocket的~~~。
参考资料 MDN_Worker 
caniuse_Worker 
END 
2017-03-16 完成
 
2017-02-25 立项