首先本文只討論 window.Worker 而不包含SharedWorker.畢竟這玩意連firefox4 beta7 都還沒有支援.(貌似chrome safari opera 的較新版本都已經支援.)
有一點要提前說明的是:worker線程中的代碼具有獨立的執行環境. 我們可以把它假想成一個mini的global環境. 有興趣的同學可以去看相關文檔.
在這個執行環境中.不要在奢望你能調用絕大多數宿主提供給我們的對象、屬性、方法.你大概可以認為,我們只能用的上js內建對象和方法.其他的一律不考慮.
不過 在該執行內容內.我們可以使用這樣一些東東:
self : 即當前worker 的GlobalWorker對象. 這上面有些特殊的宿主對象.比如 location、navigator、setTimeout、setInterval之類的.
一個好訊息是我們可以調用 JSON 對象. 剩下的基本就是我們下面要介紹的一些方法嘍.
worker有同源策略限制. 即worker無法引入一個跨域的資源來直接使用.(但是worker的一個方法可以繞過同源策略限制,來動態載入其他域指令碼)
瀏覽器支援:
IE: 無 (IE10已開始支援.)FireFox: 3.5+Safair: 4+ Chrome 3+Opera : 10+主要介面: (詳細API可自行查看)
介面定義相關連結 : http://www.whatwg.org/specs/web-workers/current-work/#the-workerglobalscope-abstract-interface
var worker = new Worker('a.js');
outside :
事件:
1.onmessage 事件 :當a.js 即 worker線程中調用 postMessage(sData)方法時, onmessage可以偵聽該事件,並執行指定回調方法
worker.onmessage = function(e){ e.data// 我們需要的sData }
2.onerror 事件 : 捕獲a.js中
worker線程中文法
異常和運行時各類異常.(注:這玩意也管 404那種載入失敗滴異常.除了某些瀏覽器...)
worker.onerror = function(e){ e.message // 錯誤資訊 e.lineno // 行號 e.filename //返回完整的錯誤檔案的url .};注:worker.addEventListener('message',function(e){...});// 這樣也中.
方法:
1. postMessage 方法 : 向 worker 發送訊息. worker線程 必須同時註冊onmessage事件.
2. terminate 方法 : 停掉worker的行為.
inside :
事件:
1.onmessage 事件 :當out頁調用 worker.postMessage(sData)方法時, onmessage可以偵聽該事件,並執行指定回調方法
方法:
1. postMessage 方法 : 向 out頁發送訊息. out頁必須同時註冊worker.onmessage事件.
2. close 方法 : 停掉worker的行為.
3. importScripts : 方法,worker線程動態載入外部指令碼用. 該方法會凍結worker線程.直到動態載入指令碼載入完畢或執行完畢(瀏覽器差異)
.importScripts 方法支援同事載入多個指令碼:importScripts('a.js','b.js','c.js');
.同事載入多個指令碼在各個瀏覽器中均為並行載入.(前提是HTTP串連數夠用).
.但執行順序是嚴格按照參數順序進行的.即 先執行a 後執行b 然後才是c. 無論哪個先載入完.(FF有差異)
遺憾的是 safari4 在最初支援了 worker的同時,並沒有提供原生 JSON對象的支援. 如果我們的項目不考慮支援safari4,那麼真是個好訊息.否則,可能為了讓worker更健壯的工作,我們需要自己寫一個JSON轉換了. 更加遺憾的是.對於web worker 的一些 行為,瀏覽器實現,也有比較大的差異.
瀏覽器差異:
關於異常:opera中, 一旦發生一個語法錯誤或執行階段錯誤.有時候會多拋出一個 Internal error 給你. 並且lineno 總是0.opera中, 一般的語法錯誤或執行期異常(如調用未定義變數,throw new Error(s)除外) 總是無法給出準確的錯誤資訊.且lineno 總是0.chrome 和 safari 中, worker.onerror 並不捕獲載入失敗的404異常.chrome 作為 worker 的先驅.值得鄙視一下.
關於worker引入檔案的緩衝問題:opera中,一但.js被緩衝那麼,即使右鍵-重新載入 ,也會直接去cache讀. 你不用指望會有什麼304 或200.這個真沒有.我們有2個選擇,要麼完全放棄用戶端緩衝, 要麼就更換版本號碼...好糾結的問題.
調用close()方法後的差異opera中,一但在worker線程中調用close()方法.那麼就代表著一切都結束了.如果在close(); 後你仍然試圖使用worker中的某些屬性、事件、或方法則會拋出一個Internal error . 但其他瀏覽器則不會.opera中,調用close()方法後, 出現的postMessage('')給首頁面傳遞資訊,不會再觸發首頁面的onmessage回調. 其他瀏覽器則可以. FF 中, 調用close()後 worker 雖然不再響應頁面的postMessage .但是在close();調用之前.頁面postMessage給worker線程的資訊.則仍然會在.worker線程結束後被觸發回調. 其他瀏覽器則不會.chrome中 ,調用close()後. self對象被賦值為null . 但你仍然可以和其他瀏覽器一樣 直接調用 WorkerGlobalScope對象的方法.所以,應避免使用self.postMessage, self.onmessage=function(){} ,self.close();而採用直接調用的方式.(如onmessage=function(){};)
關於worker.terminate()方法後的差異
opera中,首頁面調用此方法後. 再調用worker.postMessage(sData)會拋出異常. 其他瀏覽器則不會.(雖然此時,worker.postMessage仍然是一個方法)
opera 中worker.terminate() 帶來的問題:
1. worker.onerror 觸發回調時要在首頁面並不是在使用worker的指令碼退出所在執行環境後才會被觸發.而其他瀏覽器則如此. 考慮下面的代碼:
var worker = new Worker("error.js");//拋出一個異常.
worker.onerror = function(e){
alert(e);//opera則會觸發. 其他瀏覽器則不會.
};
var t = new Date;
while(new Date - t < 3000);
worker.terminate();//問題就出在 這裡...
2. worker.onmessage 也存在同樣的問題. 考慮下面的代碼 :
var worker = new Worker("b.js"); //b.js中 第一時間postMessage(sData);worker.onmessage=function(e){ alert(e.data);//只有opera 會觸發b.js中的這次回調. 並列印出e.data}var t = new Date;while(new Date - t < 3000);worker.terminate();
關於importScripts方法:FF中,importScripts方法載入指令碼時. 雖然同樣會凍結當前worker線程.但執行期是在worker線程結束後..即,其他瀏覽器則不是如此..其他瀏覽器的 載入 執行,都會凍結住worker的線程. FF中,importScripts 方法載入多個指令碼時,執行順序不確定.應是先到先執行. FF safari chrome 中,importScripts方法載入指令碼失敗時,會拋出異常,且可被out頁面的 worker.onerror 捕獲,opera瀏覽器則不會. FF中,importScripts方法載入多個指令碼如a.js,b.js,c.js,雖然是並行載入.但執行卻要等待a b c三個檔案都載入完畢後才開始執行. 期間 任何一個檔案404錯誤.都將導致直接退出當前執行環境並且a.js b.js c.js 以及worker自身代碼都將得不到執行. 其他瀏覽器則不是這樣. safari chrome,使用importScripts同時載入多個指令碼時,任何一個404錯誤,都將導致退出當前執行環境. 但先載入好的外部指令碼可以被正常執行.opera 則無視404錯誤.當它不存在.繼續幹後面的活.
關於worker內建立worker的差異:safari chrome中,不允許worker內部再次建立一個worker. FF 和 opera 則可以. (即safari chrome WorkerGlobal不具備一個名為Worker的成員)
總結: .對於worker對象沒有onload事件.我們不必疑惑.因為你在worker載入的.js沒有載入好之前的,所有postMessage 都會在worker 載入並且執行完畢後,被觸發回調.所以我們完全不必擔心這個問題. .對於跨瀏覽器的worker.我們遵守 邏輯不依賴執行順序,可以很好的避免一些,看起來很詫異的問題. .worker 內部可以繼續建立另外一個worker.用於多線程並行運算於互動.對於多個worker及頁面間的共用通訊.請參考SharedWorker 對象.目前瀏覽器支援還有待完善. 但是 worer內部的worker仍然不可以跨域.即worker引用資源的url 必須是相對路徑(即使引入當前worker的指令碼是一個位於其他域的.被importScripts引入到外層worker中的指令碼.相對路徑也永遠是相對 outside 頁面的路徑).或同域的絕對路徑. .對於IE 不支援這東西. 暫時沒什麼好辦法,無非是用 setTimeout 之類的去類比多線程. 畢竟我們用worker 的最終目的,在不影響UI update 及 瀏覽器與使用者互動的情況下, 可以做大規模運算.