首页 > 代码库 > 【JavaScript】线程WebWorker
【JavaScript】线程WebWorker
介绍
通过使用WebWorker,我们可以在浏览器后台运行Javascript,而不占用浏览器自身线程。WebWorker可以提高应用的总体性能,并且提升用户体验。如果你想在自己的Web应用中使用WebWorker,不妨来了解一下有关WebWorker的7件事。
1.WebWorker可以让你在后台运行Javascript
一般来说Javascript和页面的UI会共用一个线程,所以当点击一个按钮开始运行Javascript后,在这段代码运行完毕之前,页面是无法响应用户操作的,换句话来说就是被“冻结”了。而这段代码可以交给WebWorker在后台运行,那么页面在Javascript运行期间依然可以响应用户操作。后台会启动一个worker线程来执行这段代码,用户可以创建多个worker线程。所以你可以在前台做一些小规模分布式计算之类的工作,不过WebWorker有以下一些使用限制:
- WebWorker无法访问DOM节点;
- WebWorker无法访问全局变量或是全局函数;
- WebWorker无法调用alert()或者confirm之类的函数;
- WebWorker无法访问window、document之类的浏览器全局变量;
不过WebWorker中的Javascript依然可以使用setTimeout(),setInterval()之类的函数,也可以使用XMLHttpRequest对象来做Ajax通信。
2.有两种WebWorker
Webworkers可分为两种类型:专用线程dedicatedwebworker,以及共享线程sharedwebworker。Dedicatedwebworker随当前页面的关闭而结束;这意味着Dedicatedwebworker只能被创建它的页面访问。与之相对应的Sharedwebworker可以被多个页面访问。在Javascript代码中,“Work”类型代表Dedicatedwebworker,而“SharedWorker”类型代表Sharedwebworker。
在绝大多数情况下,使用Dedicatedwebworker就足够了,因为一般来说在webworker中运行的代码是专为当前页面服务的。而在一些特定情况下,webworker可能运行的是更为普遍性的代码,可以为多个页面服务。在这种情况下,我们会创建一个共享线程的Sharedwebworker,它可以被与之相关联的多个页面访问,只有当所有关联的的页面都关闭的时候,该Sharedwebworker才会结束。相对Dedicatedwebworker,sharedwebworker稍微复杂些。
3.“Worker”对象代表DedicatedWebWorker
现在来看如何使用Dedicatedwebworker。下面的例子中用到了jQuery以及Modernizr作为Javascript库,然后往HTML页面中加入以下代码:
[html]
- <!DOCTYPEhtml>
- <html>
- <head>
- <title></title>
- <scripttype="text/javascript"src="http://www.mamicode.com/script/modernizr.js"></script>
- <scripttype="text/javascript"src="http://www.mamicode.com/script/jquery-2.0.0.js"></script>
- <scripttype="text/javascript">
- $(document).ready(function(){
- if(!Modernizr.webworker){
- alert("Thisbrowserdoesn‘tsupportWebWorker!");
- return;
- }
- $("#btnStart").click(function(){
- varworker=newWorker("script/lengthytask.js");
- worker.addEventListener("message",function(evt){
- alert(evt.data);
- },false);
- worker.postMessage(10000)
- });
- });
- </script>
- </head>
- <body>
- <form>
- <inputtype="button"id="btnStart"value="http://www.mamicode.com/StartProcessing"/>
- </form>
- </body>
- </html>
这个HTML页面中有个按钮,点击后会运行一个Javascript文件。上面的代码中首先检测当前浏览器是否支持WebWorker,不支持的话,就跳出提醒信息。
按钮的点击事件中创建了Worker对象,并给它指定了Javascript脚本文件——lengthytask.js(稍后会有代码),并且给Worker对象绑定了一个“message”事件。该事件会在后台代码(lengthytask.js)向页面返回数据时触发。“message”事件可以通过event.data来获取后台代码传回的数据。最后,postMessage方法正式执行lengthytask.js,该方法还可以向后台代码传递参数,后台代码同样通过message事件获取该参数。
下面是lengthytask.js主要包含的代码:
[javascript]
- addEventListener("message",function(evt){
- vardate=newDate();
- varcurrentDate=null;
- do{
- currentDate=newDate();
- }while(currentDate-date<evt.data);
- postMessage(currentDate);
- },false);
以上代码在后台监听message时间,并获取页面传来的参数:10000;这里实际上是一个计时函数:在message事件被触发10000毫秒之后,把结果(currentDate)传给页面。
所以当点击“StartProcessing”按钮,页面会在10秒钟后把当时的时刻alert出来。在这10秒钟内页面依然可以响应鼠标键盘事件。
4.“SharedWorker”对象代表SharedWebWorker
前面的代码使用的是dedicatedwebworker。这一节会用sharedwebworker代替dedicatedwebworker,来区别两者的不同。下面是同一个例子的sharedwebworker版本:
[javascript]
- addEventListener("message",function(evt){
- vardate=newDate();
- varcurrentDate=null;
- do{
- currentDate=newDate();
- }while(currentDate-date<evt.data);
- postMessage(currentDate);
- },false);
请注意加黑的代码,这里创建了一个SharedWorker对象,并把message事件绑定在sharedworker的port对象上;同样由port对象发起postMessage,开始执行后台代码sharedlengthytask.js。
下面是sharedlengthytask.js的主要代码:
[javascript]
- varport;
- addEventListener("connect",function(evt){
- port=evt.ports[0];
- port.addEventListener("message",function(evt){
- vardate=newDate();
- varcurrentDate=null;
- do{
- currentDate=newDate();
- }while(currentDate-date<evt.data);
- port.postMessage(currentDate);
- },false);
- port.start();
- },false);
使用SharedWorker对象的后台代码需要绑定connect和message事件,connect事件会在页面上的port被start时触发。之后的message事件的回调函数与之前的基本相同,最后port调用postMessage方法把结果传回给页面。
5.WebWorker使用XMLHttpRequest与服务端通信
有些情况下,webworker还需要与服务器进行交互。比如页面可能需要处理来自数据库中的信息,我们就需要使用Ajax技术与服务器交互,下面代码包含了webworker如何从服务端获取数据:
[javascript]
- addEventListener("message",function(evt){
- varxhr=newXMLHttpRequest();
- xhr.open("GET","lengthytaskhandler.ashx");
- xhr.onload=function(){
- postMessage(xhr.responseText);
- };
- xhr.send();
- },false);
上面的代码向服务端的asp.net服务lengthytaskhandler.ashx发出GET请求。并注册了获取数据后的onload事件。下面的代码是服务端的lengthytaskhandler.ashx:
[csharp]
- namespaceWebWorkerDemo
- {
- publicclassLengthyTaskHandler:IHttpHandler
- {
- publicvoidProcessRequest(HttpContextcontext)
- {
- System.Threading.Thread.Sleep(10000);
- context.Response.ContentType="text/plain";
- content.Response.Write("Processingsuccessful!");
- }
- publicboolIsReusable
- {
- get
- {
- returnfalse;
- }
- }
- }
- }
如你所见,ProcessRequest模拟了一个长时间运行的任务,并返回了“Processingsuccessful!”的消息。
6.通过Error事件捕捉错误信息
当我们把越来越复杂的逻辑加到WebWorker里时,错误处理机制是必不可少的。而WebWorker恰恰提供了error事件,供开发者捕捉错误信息。下面的代码展示了如何绑定error事件:
[javascript]
- $("#btnStart").click(function(){
- varworker=newWorker("scripts/lengthytask.js");
- worker.addEventListener("error",function(evt){
- alert("Line#"+evt.lineno+"-"+evt.message+"in"+evt.filename);
- },false);
- worker.postMessage(10000);
- });
如上可见,Worker对象可以绑定error事件;而且evt对象中包含错误所在的代码文件(evt.filename)、错误所在的代码行数(evt.lineno)、以及错误信息(evt.message)。
7.通过terminate()方法终止WebWorker
有些情况下,我们可能需要强制终止执行中的WebWorker。Worker对象提供了terminate()来终止自身执行任务,被终止的Worker对象不能被重启或重用,我们只能新建另一个Worker实例来执行新的任务。
总结
WebWorker可以在后台执行脚本,而不会阻塞页面交互。Worker对象分为两种:专用式WebWorker和共享式WebWorker:专用式的WebWorker只能被当个页面使用,而共享式的WebWorker可以在被多个页面使用。另外,本文还介绍了WebWorker的错误处理机制,以及使用Ajax与服务端交互。