再談JavaScript線程,談JavaScript線程
代碼判斷一:
<div id="div"> click me</div><script> var div=document.getElementById("div"); div.addEventListener('click',function(){ alert('You have clicked me!'); }); for(var i =0; i<999999999;i++){ console.log(i); }</script>
執行之,不出意外的話所有瀏覽器都會卡死,因為上面的for迴圈次數太多了,非常耗費CPU資源,而基於JavaScript單線程的事實,瀏覽器UI渲染被掛起而導致假死。
現在問題來了,我就是想要實現上述代碼,怎麼辦?
Concurrent.Thread.js
該類庫實質上還是使用setTimeout來實現一個“假的多線程”。在HTML5 WebWorker問世之前是一個很好的選擇。比如我們要實現上述“程式碼片段一”,可以這樣寫(點我下載類庫):
程式碼片段二:
<div id="div"> click me</div><script src="Concurrent.Thread.js"></script><script> Concurrent.Thread.create(function(){ var div=document.getElementById("div"); div.addEventListener('click',function(){ alert('You have clicked me!'); }); for(var i =0; i<9999999;i++){ console.log(i); } });</script>
通過該類庫提供的create方法可以建立一個“新線程”。另外,給script標籤的type屬性設定為 text/x-script.multithreaded-js 也可以實現同樣的效果:
程式碼片段三:
<div id="div"> click me</div><script src="Concurrent.Thread.js"></script><script type="text/x-script.multithreaded-js"> var div=document.getElementById("div"); div.addEventListener('click',function(){ alert('You have clicked me!'); }); for(var i =0; i<9999999;i++){ console.log(i); }</script>
WebWorker
針對以上瀏覽器卡死這種糟糕的使用者體驗,HTML5怎麼會熟視無睹呢?
下面我們用經典的斐波那契數列來做測試:
程式碼片段四:
首頁面:
<div id="div"></div><script> window.onload=function(){ var div=document.getElementById("div"); if(typeof(Worker)!=="undefined"){//在建立WebWorker之前,先判斷瀏覽器是否支援 console.log("Start calculating...."); var time1= new Date()*1;//獲得目前時間戳 var worker=new Worker("fibonacci.js");//建立WebWorker對象,並傳遞在新線程中將要執行的指令碼的路徑 worker.onmessage=function(e){ //監聽從新線程發送過來的資料 div.innerHTML=e.data; var time2=new Date()*1; console.log("time spend:"+(time2-time1)+"ms"); } worker.postMessage(36);//向新線程發送資料 }else{ alert("Your browser do not support WebWoker"); } }</script>fibonacci.js:var fibonacci=function (n){ return n<3?n:(arguments.callee(n-1)+arguments.callee(n-2));}onmessage=function(e){ var num=parseInt(e.data,10); postMessage(fibonacci(num));//向首頁面發送資料}
基本的使用方法已在代碼中做注釋了,查看控制台,可以看見很快就列印出執行時間了。所以我們得出的結論是:WebWorker適合在前端執行複雜的大量的計算。需要注意的是,WebWorker不支援跨域,本地測試還是用http協議,不要用file協議,否則不能建立Worker對象而報指令碼錯誤 。
如果我們需要連續執行多個postMessage操作,最好不要work.postMessage一直寫,像這樣:
worker.postMessage(36); worker.postMessage(36); worker.postMessage(36);
因為此時只有一個WebWorker執行個體,postMessage會順序執行而不是非同步執行,就不能充分發揮它的效能了。可以通過建立多個WebWorker執行個體來發送資料。
需要注意的幾點事項有:
1、我們觀察到WebWorker通過接受一個url來建立一個worker,而jsonp的實現原理就是通過動態插入script標籤載入資料,那我們嘗試用WebWorker來實現同樣的事情不是更好嗎?因為WebWorker是多線程的,沒有阻塞,豈不美哉?但實際上經過實驗,我們發現WebWorker表現並不如意。所以這並不是它擅長的事,我們還是不要讓它越俎代庖的好。
2、WebWorker在接受其他來源資訊的時候,其實也給網站的安全帶來了隱患,如果接收不明來源的指令碼資訊,可能會導致XSS注入攻擊。所以這點需要防範,其實我們上面例子中使用innerHTML是不安全的,可以使用innerText或現代瀏覽器提供的textContent來替代,以過濾掉html標籤。
今天比較累了,想睡覺了,先寫這麼多吧。