針對(duì)HTML5的Web Worker使用攻略

字號(hào):


    這篇文章主要介紹了針對(duì)HTML5的Web Worker使用攻略,特別是結(jié)合worker拿手的JS使用起來十分有效,需要的朋友可以參考下
    Web Workers 是 HTML5 提供的一個(gè)javascript多線程解決方案,我們可以將一些大計(jì)算量的代碼交由web Worker運(yùn)行而不凍結(jié)用戶界面。
    一:如何使用Worker
    Web Worker的基本原理就是在當(dāng)前javascript的主線程中,使用Worker類加載一個(gè)javascript文件來開辟一個(gè)新的線程,起到互不阻塞執(zhí)行的效果,并且提供主線程和新線程之間數(shù)據(jù)交換的接口:postMessage,onmessage。
    那么如何使用呢,我們看一個(gè)例子:
    JavaScript Code
    //worker.js   
    onmessage =function (evt){   
      var d = evt.data;//通過evt.data獲得發(fā)送來的數(shù)據(jù)   
      postMessage( d );//將獲取到的數(shù)據(jù)發(fā)送會(huì)主線程   
    }  
    HTML頁面:test.html
    XML/HTML Code
    <!DOCTYPE HTML>  
    <html>  
    <<span id="20_nwp"><a mpid="20" target="_blank" id="20_nwl"><span>head</span></a></span>>  
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
     <script type="text/<span id="21_nwp"><a mpid="21" target="_blank" id="21_nwl"><span>javascript</span></a></span>">  
    //WEB頁主線程   
    var worker =new Worker("worker.js"); //創(chuàng)建一個(gè)Worker對(duì)象并向它傳遞將在新線程中執(zhí)行的腳本的URL   
     worker.postMessage("hello world");     //向worker發(fā)送數(shù)據(jù)   
     worker.onmessage =function(evt){     //接收worker傳過來的數(shù)據(jù)<span id="22_nwp"><a mpid="22" target="_blank" id="22_nwl"><span>函數(shù)</span></a></span>  
       console.log(evt.<span id="23_nwp"><a mpid="23" target="_blank" id="23_nwl"><span>data</span></a></span>);              //輸出worker發(fā)送來的數(shù)據(jù)   
     }   
     </script>  
     </head>  
     <body></body>  
    </html>  
    用Chrome瀏覽器打開test.html后,控制臺(tái)輸出 ”hello world” 表示程序執(zhí)行成功。
    通過這個(gè)例子我們可以看出使用web worker主要分為以下幾部分
    WEB主線程:
    1.通過 worker = new Worker( url ) 加載一個(gè)JS文件來創(chuàng)建一個(gè)worker,同時(shí)返回一個(gè)worker實(shí)例。
    2.通過worker.postMessage( data ) 方法來向worker發(fā)送數(shù)據(jù)。
    3.綁定worker.onmessage方法來接收worker發(fā)送過來的數(shù)據(jù)。
    4.可以使用 worker.terminate() 來終止一個(gè)worker的執(zhí)行。
    worker新線程:
    1.通過postMessage( data ) 方法來向主線程發(fā)送數(shù)據(jù)。
    2.綁定onmessage方法來接收主線程發(fā)送過來的數(shù)據(jù)。
    二:Worker能做什么
    知道了如何使用web worker ,那么它到底有什么用,可以幫我們解決那些問題呢。我們來看一個(gè)fibonacci數(shù)列的例子。
    大家知道在數(shù)學(xué)上,fibonacci數(shù)列被以遞歸的方法定義:F0=0,F(xiàn)1=1,F(xiàn)n=F(n-1)+F(n-2)(n>=2,n∈N*),而javascript的常用實(shí)現(xiàn)為:
    JavaScript Code
    var fibonacci =function(n) {   
        return n <2? n : arguments.callee(n -1) + arguments.callee(n -2);   
    };   
    //fibonacci(36)  
    在chrome中用該方法進(jìn)行39的fibonacci數(shù)列執(zhí)行時(shí)間為19097毫秒 ,而要計(jì)算40的時(shí)候?yàn)g覽器直接提示腳本忙了。
    由于javascript是單線程執(zhí)行的,在求數(shù)列的過程中瀏覽器不能執(zhí)行其它javascript腳本,UI渲染線程也會(huì)被掛起,從而導(dǎo)致瀏覽器進(jìn)入僵死狀態(tài)。使用web worker將數(shù)列的計(jì)算過程放入一個(gè)新線程里去執(zhí)行將避免這種情況的出現(xiàn)。具體看例子:
    JavaScript Code
    //fibonacci.js   
    var fibonacci =function(n) {   
        return n <2? n : arguments.callee(n -1) + arguments.callee(n -2);   
    };   
    onmessage =function(event) {   
        var n = parseInt(event.<span id="16_nwp"><a mpid="16" target="_blank" id="16_nwl"><span>data</span></a></span>, 10);   
        postMessage(fibonacci(n));   
    };  
    HTML頁面:fibonacci.html
    XML/HTML Code
    <!DOCTYPE HTML>  
    <html>  
    <<span id="11_nwp"><a mpid="11" target="_blank" id="11_nwl"><span>head</span></a></span>>  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
    <title>web worker fibonacci</title>  
    <script type="text/<span id="12_nwp"><a mpid="12" target="_blank" id="12_nwl"><span>javascript</span></a></span>">  
      onload =function(){   
          var worker =new Worker('fibonacci.js');     
          worker.addEventListener('message', function(event) {   
            var timer2 = (new Date()).valueOf();   
               console.log( '結(jié)果:'+event.<span id="13_nwp"><a mpid="13" target="_blank" id="13_nwl"><span>data</span></a></span>, '時(shí)間:'+ timer2, '用時(shí):'+ ( timer2  - timer ) );   
          }, false);   
          var timer = (new Date()).valueOf();   
          console.log('開始計(jì)算:40','時(shí)間:'+ timer );   
          setTimeout(function(){   
              console.log('定時(shí)器<span id="14_nwp"><a mpid="14" target="_blank" id="14_nwl"><span>函數(shù)</span></a></span>在計(jì)算數(shù)列時(shí)執(zhí)行了', '時(shí)間:'+ (new Date()).valueOf() );   
          },1000);   
          worker.postMessage(40);   
          console.log('我在計(jì)算數(shù)列的時(shí)候執(zhí)行了', '時(shí)間:'+ (new Date()).valueOf() );   
      }     
      </script>  
    </<span id="15_nwp"><a mpid="15" target="_blank" id="15_nwl"><span>head</span></a></span>>  
    <body>  
    </body>  
    </html>  
    在Chrome中打開fibonacci.html,控制臺(tái)得到如下輸出:
    開始計(jì)算:40 時(shí)間:1316508212705
    我在計(jì)算數(shù)列的時(shí)候執(zhí)行了 時(shí)間:1316508212734
    定時(shí)器
    XML/HTML Code
    <span id="9_nwp"><a mpid="9" target="_blank" id="9_nwl"><span>函數(shù)</span></a></span>  
    在計(jì)算數(shù)列時(shí)執(zhí)行了 時(shí)間:1316508213735
    結(jié)果:102334155 時(shí)間:1316508262820 用時(shí):50115
    這個(gè)例子說明在worker中執(zhí)行的fibonacci數(shù)列的計(jì)算并不會(huì)影響到主線程的代碼執(zhí)行,完全在自己獨(dú)立的線程中計(jì)算,只是在計(jì)算完成之后將結(jié)果發(fā)回主線程。
    利用web worker我們可以在前端執(zhí)行一些復(fù)雜的大量運(yùn)算而不會(huì)影響頁面的展示,并且不會(huì)彈出惡心的腳本正忙提示。
    下面這個(gè)例子使用了web worker來計(jì)算場(chǎng)景中的像素,場(chǎng)景打開時(shí)是一片一片進(jìn)行繪制的,一個(gè)worker只計(jì)算一塊像素值。
    http://nerget.com/rayjs-mt/rayjs.html
    三:Worker的其他嘗試
    我們已經(jīng)知道Worker通過接收一個(gè)URL來創(chuàng)建一個(gè)worker,那么我們是否可以利用web worker來做一些類似jsonp的請(qǐng)求呢,大家知道jsonp是通過插入script標(biāo)簽來加載json數(shù)據(jù)的,而script元素在加載和執(zhí)行過程中都是阻塞式的,如果能利用web worker實(shí)現(xiàn)異步加載將會(huì)非常不錯(cuò)。
    下面這個(gè)例子將通過 web worker、jsonp、ajax三種不同的方式來加載一個(gè)169.42KB大小的JSON數(shù)據(jù)
    JavaScript Code
    // /aj/webWorker/core.js   
    function $E(id) {   
        return document.getElementById(id);   
    }   
    onload =function() {   
        //通過web worker加載   
        $E('workerLoad').onclick =function() {   
            var url ='http://js.wcdn.cn/aj/mblog/face2';   
            var d = (new Date()).valueOf();   
            var worker =new Worker(url);   
            worker.onmessage =function(obj) {   
                console.log('web worker: '+ ((new Date()).valueOf() - d));   
            };   
        };   
        //通過jsonp加載   
        $E('jsonpLoad').onclick =function() {   
            var url ='http://js.wcdn.cn/aj/mblog/face1';   
            var d = (new Date()).valueOf();   
            STK.core.io.scriptLoader({   
                method:'post',   
                url : url,   
                onComplete : function() {   
                    console.log('jsonp: '+ ((new Date()).valueOf() - d));   
                }   
            });   
        };   
        //通過<span id="8_nwp"><a mpid="8" target="_blank" id="8_nwl"><span>ajax</span></a></span>加載   
        $E('ajaxLoad').onclick =function() {   
            var url ='http://js.wcdn.cn/aj/mblog/face';   
            var d = (new Date()).valueOf();   
            STK.core.io.ajax({   
                url : url,   
                onComplete : function(json) {   
                    console.log('ajax: '+ ((new Date()).valueOf() - d));   
                }   
            });   
        };   
    };  
    HTML頁面:/aj/webWorker/worker.html
    XML/HTML Code
    <!DOCTYPE HTML>  
    <html>  
    <<span id="4_nwp"><a mpid="4" target="_blank" id="4_nwl"><span>head</span></a></span>>  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
    <title>Worker example: load <span id="5_nwp"><a mpid="5" target="_blank" id="5_nwl"><span>data</span></a></span></title>  
    <script src="http://js.t.sinajs.cn/STK/js/gaea.1.14.js" type="text/<span id="6_nwp"><a mpid="6" target="_blank" id="6_nwl"><span>javascript</span></a></span>"></script>  
    <script type="text/javascript" src="http://js.wcdn.cn/aj/webWorker/core.js"></script>  
    </head>  
    <body>  
        <input type="button" id="workerLoad" value="web worker加載"></input>  
        <input type="button" id="jsonpLoad" value="jsonp加載"></input>  
        <input type="button" id="<span id="7_nwp"><a mpid="7" target="_blank" id="7_nwl"><span>ajax</span></a></span>Load" value="ajax加載"></input>  
    </body>  
    </html>  
    設(shè)置HOST
    代碼如下:
    127.0.0.1 js.wcdn.cn
    通過 http://js.wcdn.cn/aj/webWorker/worker.html 訪問頁面然后分別通過三種方式加載數(shù)據(jù),得到控制臺(tái)輸出:
    代碼如下:
    web worker: 174
    jsonp: 25
    ajax: 38
    多試幾次發(fā)現(xiàn)通過jsonp和ajax加載數(shù)據(jù)的時(shí)間相差不大,而web worker的加載時(shí)間一直處于高位,所以用web worker來加載數(shù)據(jù)還是比較慢的,即便是大數(shù)據(jù)量情況下也沒任何優(yōu)勢(shì),可能是Worker初始化新起線程比較耗時(shí)間。除了在加載過程中是無阻塞的之外沒有任何優(yōu)勢(shì)。
    那么web worker是否能支持跨域js加載呢,這次我們通過http://127.0.0.1/aj/webWorker/worker.html 來訪問頁面,當(dāng)點(diǎn)擊 ”web worker加載” 加載按鈕時(shí)Chrome下無任何反映,F(xiàn)F6下提示錯(cuò)誤。由此我們可以知道web worker是不支持跨域加載JS的,這對(duì)于將靜態(tài)文件部署到單獨(dú)的靜態(tài)服務(wù)器的網(wǎng)站來說是個(gè)壞消息。
    所以web worker只能用來加載同域下的json數(shù)據(jù),而這方面ajax已經(jīng)可以做到了,而且效率更高更通用。還是讓W(xué)orker做它自己擅長(zhǎng)的事吧。
    四:總結(jié)
    web worker看起來很美好,但處處是魔鬼。
    我們可以做什么:
    1.可以加載一個(gè)JS進(jìn)行大量的復(fù)雜計(jì)算而不掛起主進(jìn)程,并通過postMessage,onmessage進(jìn)行通信
    2.可以在worker中通過importScripts(url)加載另外的腳本文件
    3.可以使用 setTimeout(), clearTimeout(), setInterval(), and clearInterval()
    4.可以使用XMLHttpRequest來發(fā)送請(qǐng)求
    5.可以訪問navigator的部分屬性
    有那些局限性:
    1.不能跨域加載JS
    2.worker內(nèi)代碼不能訪問DOM
    3.各個(gè)瀏覽器對(duì)Worker的實(shí)現(xiàn)不大一致,例如FF里允許worker中創(chuàng)建新的worker,而Chrome中就不行
    4.不是每個(gè)瀏覽器都支持這個(gè)新特性