用 Web Workers 提升前端 JavaScript 處理性能
你有沒有想過在運行大型複雜的JavaScript腳本的時候不會發生瀏覽器假死?
你有沒有想過JavaScript可以在後台運行?
你有沒有想過JavaScript函數甚至可以在多個進程中同時運行?
不可能?當你看完本文,也許會讓你感覺很興奮!
什麼是Web Workers?
Web Workers為WEB前端網頁上的腳本提供了一種能在後台進程中運行的方法。一旦它被創建,Web Workers就可以通過postMessage()向任務池發送任務請求,執行完之後再通過postMessage()返回消息給創建者指定的事件處理程序(通過onmessage進行捕獲)。
Web Workers進程能夠在不影響用戶界面的情況下處理任務,並且,它還可以使用XMLHttpRequest來處理I/O,無論responseXML和channel屬性是否為null。
注意:通常,後台進程(包括web workers進程)不能對DOM進行操作。如果希望後台程序處理的結果能夠改變DOM,只能通過返回消息給創建者的回調函數進行處理。
瀏覽器支持:
FireFox3.5(Firefox 3.1 support for DOM workers)
Safari4
支持html5的瀏覽器
進程安全
Workers接口可以創建真正的系統級別的進程,如果你不小心的話,你的代碼很容易引起並發操作效果,這將會很有趣。
在Mozilla下,Workser並發操作常發生在:
1、在做網站下載的時候使用Worker。
2、使用Worker實現處理擴展功能。
創建一個Worker
我們可以很簡單地創建一個worker,只要調用Worker(URI)構造函數即可。參數URI,要執行的腳本文件地址。
如果你想獲取worker進程的返回值,可以通過它的onmessage屬性來綁定一個事件處理程序,如:
myWorker.onmessage = function(event){
alert('Called back by the worker!');
};
第一行用來創建和運行worker進程,第二行設置worker的onmessage屬性用來綁定指定的事件處理程序,當worker私有的postMessage()方法被調用時,這個被綁定的程序就會被觸發。
創建一個subworkers
如果原意,你可以創建多個workers。subworkers必須寄宿於同一個父頁面下,並且,它的URI必須與parent worker的地址同源。這樣可以很好的維持它們的依賴關係。
Timeouts 和 intervals
Workers可以使用timeouts和intervals。這很有用,例如,如果你想讓你的worker進程週期性地運行而不是不停的循環下去的話,你就可以使用了。
參見:setTimeout(),clearTimeout(),setInterval(),clearInterval()
終止 worker
如果你需要馬上終止一個正在運行中的worker,你可以調用它的terminate()方法:
myWorker.terminate();
這樣,一個worker進程就被結束了。
錯誤捕獲 Handling errors
當worker發生運行時錯誤時,它的onerror事件就會被觸發。該事件接收一個error的事件,該事件不會冒泡,並且可以取消。要取消該事件可以使用preventDefault()方法。
此錯誤事件有3個屬性:
- message:可讀的錯誤信息
- filename:發生錯誤的腳本文件名稱
- lineno:發生錯誤的腳本所在文件的行數
訪問navigator對象
Workers可以訪問navigator對象,它包含下面可以用來標示瀏覽器的字符:
- appName
- appVersion
- platform
- userAgent
導入腳本和庫
Worker進程可以訪問全局函數importScripts(),該方法可以將腳本或庫導入到它們的作用域中。
此方法可以接受空的參數或多個腳本URI參數,下面這些形式都是合法的:
importScripts();/* imports nothing */
importScripts('foo.js');/* import just "foo.js" */
importScripts('foo.js','bar.js');/* imports two scripts */
Firefox會加載列出的每一個腳本文件,然後運行並初始化。這些腳本中的任何全局對象都可以被worker使用。
注意:腳本下載可能順序不一樣,但,執行的順序一定是按importScripts中列出的順序進行,而且是同步的,在所有腳本加載完並運行結束後importScripts才會返回。
演示
這部分,我們將演示如何使用DOM Workers.
在後台執行指令
Workers的一個很有用的方法就是使得你的代碼可以在後台運行,而不影響用戶界面。下面,我們來演示一下使用worker進行Fibonacci數列的計算。
JavaScript代碼:
下面的Javascript代碼保存到 fibonacci.js 文件
var results = [];
var resultReceiver = function(event){
results.push(parseInt(event.data,10));
if(results.length==2){
postMessage(results[0]+results[1]);
}
};
var errorReceiver = function(event){
throw event.data;
};
var onmessage = function(event){
var n = parseInt(event.data,10);
if(n==0||n==1){
postMessage(n);
return;
}
for(var i=0;i<=2;i++){
var worker = new Worker("fibonacci.js");
worker.onmessage = resultReceiver;
worker.onerror = errorReceiver;
worker.postMessage(n-i);
}
};
onmessage函數在worker調用postMessage()時被觸發,這時便開始遞歸。在裡邊創建新的worker拷貝對每次的計算結果進行迭代。
HTML代碼
<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML4.0 Transitional//EN">
<html>
<head>
<title>Test threads fibonacci</title>
</head>
<body>
<div id="result">http://www.v-ec.com/dh20156/article.asp?id=242</div>
<script type="text/javascript">
var worker = new Worker("fibonacci.js");
worker.onmessage = function(event){
document.getElementById('result').innerHTML = event.data;
dump('Got:'+event.data+'\n');
};
worker.onerror = function(event){
dump('Worker error:'+error.message+'\n');
throw error;
};
worker.postMessage('5');
</script>
</body>
</html>
在頁面中創建了一個ID為result的DIV用來顯示計算結果,然後創建worker,設置onmessage事件用來顯示計算結果到result,設置onerrer事件用來設置dump錯誤信息。
最後,發送“5”到worker,開始計算。
在後台操作 WEB I/O
你可以在此查看到一篇關於Using workers in extensions的文章。
在多個workers進程中處理任務
隨著多核計算機的普及,在多進程處理複雜的任務也越來越被更多的人使用,在多個workers進程中處理任務的演示不久將會提供給大家。
在workers中創建workers
前面Fibonacci例子的演示中我們看到,在workers中可以創建其他的workers,這使得遞歸很容易進行。
發送對象給workers
你可以通過postMessage()方法安全地將對象傳遞到workers或者從中返回對象;這些對象將被自動轉換為JSON格式。
var onmessage = function(e){
postMessage(e.data);
};
注意:在workers中進出的對象不能包含函數和循環引用,因為JSON不支持它們。
See also參見:
WebWorkers
Worker
WorkerGlobalScope
SharedWorker
Web Workers specification
轉載請註明出處:http://www.v-ec.com/dh20156/article.asp?id=242
經過WEB前端DHTML精英俱樂部測試發現,Safari4已經可以支持Web Workers,不過貌似還不支持在worker中創建worker,所以,本文中上面的Fibonacci例子測試失敗,WEB前端DHTML精英俱樂部重新寫了一個實例進行演示: