JavaScript 被設(shè)計為運行在一個單線程環(huán)境中,這意味著多個腳本不能同時運行。考慮這樣一種情況,我們需要處理 UI 事件,查詢和處理大量的 API 數(shù)據(jù)以及操作 DOM。
在 CPU 使用率過高的情況下 JavaScript 還會造成瀏覽器假死。我們來舉一個簡單的例子,用 JavaScript 運行一個大循環(huán):
<!DOCTYPE HTML>
<html>
<head>
<title>Big for loop</title>
<script>
function bigLoop(){
for (var i = 0; i <= 10000000000; i += 1){
var j = i;
}
alert("Completed " + j + "iterations" );
}
function sayHello(){
alert("Hello sir...." );
}
</script>
</head>
<body>
<input type="button" onclick="bigLoop();" value="Big Loop" />
<input type="button" onclick="sayHello();" value="Say Hello" />
</body>
</html>
嘗試運行在線示例來查看其結(jié)果 — 可以使用任何瀏覽器運行在線示例。當(dāng)我們在 FireFox 中點擊 Big Loop 按鈕是它會顯示如下所示結(jié)果:
http://wiki.jikexueyuan.com/project/html5/images/bigloop.jpg" alt="big loop" />
上述情況可以使用 Web Workers 處理,它可以處理所有昂貴的計算任務(wù)而不阻塞用戶界面,它通常運行在單獨的線程上。
Web Workers 允許長時間運行腳本,而不阻塞腳本響應(yīng)點擊或者其他用戶交互;它還允許執(zhí)行長期任務(wù)而無需頁面保持響應(yīng)。
Web Workers 就是背景腳本,它們是相對重量級的,并不打算被大量使用。比如,它并不適合為一個四百萬像素圖像的每一個像素啟動一個 worker。
當(dāng)腳本執(zhí)行在 Web Worker 中時,它不能訪問 Web 頁面的 window 對象(window.document),這意味著這個 Web Workers 不必直接訪問該頁面和 DOM API。盡管 Web Workers 不能阻塞瀏覽器 UI,但它們?nèi)匀粫?CPU 周期,降低系統(tǒng)的響應(yīng)速度。
Web Workers 使用 JavaScript 文件 URL 初始化,這個文件包含要在 worker 中執(zhí)行的代碼。這個代碼可以設(shè)置事件監(jiān)聽器,與引入它的主頁面進(jìn)行通信。下面是簡單的語法:
var worker = new Worker('bigLoop.js');
如果指定的 JavaScript 文件已經(jīng)存在,瀏覽器會啟動一個新異步下載的 worker 線程。如果 worker 路徑返回 404,那么這個 worker 會靜默失敗。
如果你的應(yīng)用程序要支持多個 JavaScript 文件,你可以將它們導(dǎo)入到 importScripts() 方法中,這個方法接收逗號分割的文件名作為參數(shù),如下所示:
importScripts("helper.js", "anotherHelper.js");
一旦 Web Worker 啟動,就可以使用 postMessage() 方法在 web worker 和父頁面之間進(jìn)行通信。依賴于瀏覽器或瀏覽器版本,postMessage() 方法可以接受一個字符串或者 JSON 對象作為它的唯一參數(shù)。
通過 Web Worker 攢底的消息可以在主頁面中使用 onmessage 事件訪問?,F(xiàn)在讓我們用 Web Worker 編寫上面的 bigLoop 示例。下面是要啟動 web worker 執(zhí)行循環(huán)和返回變量 j 最終值的主頁面(hello.htm)。
<!DOCTYPE HTML>
<html>
<head>
<title>Big for loop</title>
<script>
var worker = new Worker('bigLoop.js');
worker.onmessage = function (event) {
alert("Completed " + event.data + "iterations" );
};
function sayHello(){
alert("Hello sir...." );
}
</script>
</head>
<body>
<input type="button" onclick="sayHello();" value="Say Hello"/>
</body>
</html>
下面是 bigLoop.js 文件的內(nèi)容。這里使用 postMessage() API 來傳遞通信信息給主頁面。
for (var i = 0; i <= 1000000000; i += 1){
var j = i;
}
postMessage(j);
接下來我們把 hello.htm 和 bigLoop.js 文件放入同一個目錄中,然后使用最新版的 Safari 或 FireFox 訪問 hello.htm。
也可以在線查看上述示例的結(jié)果 - 可以使用最新版的 Safari 或 FireFox 在線嘗試。
Web Workers 不能自行停止,但是啟動它們的頁面可以通過調(diào)用 terminate() 方法停止它們。
worker.terminate();
被終止的 Web Worker 將不再響應(yīng)消息或者執(zhí)行任何附加的計算。我們并不能重啟 worker;但是,可以使用同一 URL 創(chuàng)建一個新的 worker。
下面的代碼展示了一個在 Web Worker JavaScript 文件中錯誤處理函數(shù)輸出錯誤日志到控制臺的例子。這個帶有錯誤處理代碼的例子如下所示:
<!DOCTYPE HTML>
<html>
<head>
<title>Big for loop</title>
<script>
var worker = new Worker('bigLoop.js');
worker.onmessage = function (event) {
alert("Completed " + event.data + "iterations" );
};
worker.onerror = function (event) {
console.log(event.message, event);
};
function sayHello(){
alert("Hello sir...." );
}
</script>
</head>
<body>
<input type="button" onclick="sayHello();" value="Say Hello"/>
</body>
</html>
下面的代碼可用于瀏覽器中檢測是否支持 Web Worker 特性:
<!DOCTYPE HTML>
<html>
<head>
<title>Big for loop</title>
<script src="/js/modernizr-1.5.min.js"></script>
<script>
if (Modernizr.webworkers) {
alert("Congratulation!! you have web workers support." );
}else{
alert("Sorry!! you do not have web workers support." );
}
</script>
</head>
<body>
<p>Checking for Browser Support for web workers</p>
</body>
</html>
可以嘗試在線示例查看結(jié)果 - 請使用不同的瀏覽器運行在線示例。