雖然在JavaScript中有setInterval和setTimeout函數使javaScript看起來好像使多線程執行,單實際上Javascript使單線程的,一次只能做一件事情(關於JavaScript單線程可以看看setTimeout()和setInterval() 何時被調用執行),看個簡單的例子證明一下
<!DOCTYPE html> <Html> <head> <title>Web Workers</title> </head> <body> <h1>Web Workers</h1> <script type="text/Javascript"> setTimeout(function(){ console.log('timeout function'); },1000); alert('do not close'); </script> </body> </Html>
頁面一運行就會彈出一個對話框,如果setTimeout是在另外一個線程運行,那麼過一秒鐘控制台就會打印“timeout function”,事實是只要不關閉對話框,控制台永遠不會輸出文字,這兩句話確實是在一個線程內運行的。
這樣的設計使JavaScript比較簡單,但有時候也很令人煩惱,因為單線程的設計意味著Javascript代碼必須很快運行完,常見的問題就 是一段復雜的JavaScript腳本會中斷頁面其它腳本執行,甚至會出現頁面失去響應,這也就是為什麼AJax的API要設計成異步的。
在Html5規范中引入了web workers概念,解決客戶端Javascript無法多線程的問題,其定義的worker是指代碼的並行線程,不過web worker處於一個自包含的環境中,無法訪問主線程的window對象和document對象,和主線程通信只能通過異步消息傳遞機制。 (《JavaScript權威指南》)
我們需要把希望單獨執行的Javascript代碼放到一個單獨的JS文件中,然後在頁面中調用Worker構造函數來創建一個線程,參數是該文件 路徑,參數存放如果是相對地址,那麼要以包含調用Worker構造函數語句所在腳本為參照,如果是絕對路徑,需要保證同源(協議+主機+端口)。這個文件 不需要我們在頁面使用script標簽顯示引用
var worker=new Worker('JS/worker.JS');
這時候這個文件就會被異步加載並在後台執行,創建成功地worker是醬紫的
我們可以看到worker對象只有兩個屬性,其實是兩個回調函數句柄
在其prototype內有兩個重要方法
在一個頁面顯示0~10000內所有可以被n整除的數,當然我們不用i*n這種,要略微使計算顯得復雜一些嘛
index.Html
<!DOCTYPE html> <Html> <head> <title>Web Workers</title> </head> <body> <h1>Web Workers</h1> <div id="test" style="width:500px;"></div> <script type="text/Javascript"> var worker=new Worker('js/worker.JS'); worker.postMessage({ n:69 }); worker.onmessage=function(e){ var test=document.getElementById('test').innerHTML=e.data; }; </script> </body> </Html>
/js/worker.JS
function calc(n){ var result=[]; for(var i=1;i<10000;i++){ var tem=i; if(i%n==0){ if(i%(10*n)==0){ tem+='<br/>'; } result.push(tem); } } self.postMessage(result.join(' ')); self.close(); } onmessage=function(e){ calc(e.data.n); };
worker.onmessage
綁定主線程的message事件,當worker調用postMessage時方法時,綁定的事件處理程序會被調用到,傳遞來的數據可以使用MouseEvent的data屬性獲取,通過target屬性還可以獲取worker對象
self是什麼
self是woker中對自身的引用,有些像this
close()
在worker內部調用close()方法效果和在外部調用worker實例的terminate()方法效果一樣,終止worker運行
onmessage
在這個句柄內接收外部調用者傳遞的參數,參數可以通過e.data獲取
self.postMessage()
沒錯通過這個方法我們可以在worker內把結果傳遞給主線程,主線程上綁定的message事件的處理程序會被調用
web worker的簡單用法就是這樣,但有些姿勢還是得了解一下。
全新的JavaScript環境
當一個Worker實例被創建的時候,它會在一個全新的JavaScript運行環境中,完全和創建worker的腳本分離開,即使我們傳遞的消息是引用類型它們也是復制傳遞的,修改worker中的參數不影響創建腳本中的參數。
importScripts()
我們可以通過importScripts()方法通過url在worker中加載庫函數,
importScripts('utility/dialog.js','common/cookIE.JS');
方法可以接受多個url,相對地址的url以當前worker為參照,方法會按照參數順序依次下載運行庫函數,如果中間某個腳本出錯,剩下的都不會被載入和執行,而且這個方法是同步的,只有所有腳本都加載、運行完後才會返回。
worker執行模型
worker線程從上到下同步運行它的代碼,然後進入異步階段來對事件及計時器響應,如果worker注冊了message事件處理程序,只要其有 可能觸發,worker就一直在內存中,不會退出,但如果worker沒有監聽消息,那麼當所有任務執行完畢(包括計數器)後,他就會退出。
前面提到在worker中不能使用window對象和docuemnt對象,那麼能夠使用什麼呢
web worker還支持sub worker和共享worker,這方面沒有太仔細看,浏覽器兼容性也不討理想,有興趣同學可以上網搜索研究一下。