關(guān)于Web Workers你需要了解的七件事
介紹
Web Workers允許你在后臺(tái)運(yùn)行JavaScript代碼,而不會(huì)阻止web用戶(hù)界面。Web Workers可以提高網(wǎng)頁(yè)的整體性能,還可以增強(qiáng)用戶(hù)體驗(yàn)。Web Workers有兩種風(fēng)格 ——專(zhuān)用Web Workers和共享Web Workers。本文討論了你所需要知道的Web worker的七個(gè)關(guān)鍵方面,幫助你決定在應(yīng)用程序中使用它們的話(huà)。
1.Web Workers允許你在后臺(tái)運(yùn)行JavaScript代碼
通常,你在Web頁(yè)面中編寫(xiě)的JavaScript代碼在與用戶(hù)界面相同的線程中執(zhí)行。這就是為什么當(dāng)你點(diǎn)擊一個(gè)會(huì)觸發(fā)漫長(zhǎng)處理過(guò)程的按鈕,網(wǎng)頁(yè)的用戶(hù)界面會(huì)凍結(jié)。除非處理完成,否則你就無(wú)法工作于用戶(hù)界面。Web worker允許你在后臺(tái)執(zhí)行JavaScript,以便用戶(hù)界面保持響應(yīng),即使同時(shí)正在執(zhí)行某些腳本。執(zhí)行腳本的后臺(tái)線程通常稱(chēng)為worker thread或worker。你可以生成盡可能多的worker,只要你想。你還可以將數(shù)據(jù)傳遞到正在worker thread中執(zhí)行的腳本,并在完成時(shí)將值返回到主線程。然而,Web Workers有一些限制,如下所示:
- Web Workers無(wú)法從web頁(yè)面訪問(wèn)DOM元素。
- Web Workers無(wú)法從web頁(yè)面訪問(wèn)全局變量和JavaScript函數(shù)。
- Web Workers不能調(diào)用alert()或confirm()函數(shù)。
- 不能在Web Workers中訪問(wèn)諸如窗口,文檔和parent這樣的對(duì)象。
但是,你可以使用setTimeout(),setInterval()等函數(shù)。你也可以使用XMLHttpRequest對(duì)象向服務(wù)器發(fā)出Ajax請(qǐng)求。
2.Web Workers有兩種類(lèi)型
Web Workers有兩種類(lèi)型:專(zhuān)用Web Workers和共享Web Workers。專(zhuān)用Web Workers隨同創(chuàng)建它們的網(wǎng)頁(yè)一起存在和死亡。這意味著在網(wǎng)頁(yè)中創(chuàng)建的專(zhuān)用Web Workers無(wú)法通過(guò)多個(gè)網(wǎng)頁(yè)訪問(wèn)。另一方面,共享Web Workers在多個(gè)網(wǎng)頁(yè)之間是共享的。Worker類(lèi)代表專(zhuān)用Web Workers,而SharedWorker類(lèi)代表共享Web Workers。
在許多情況下,專(zhuān)用Web Workers就可以滿(mǎn)足你的需求。這是因?yàn)橥ǔD阈枰?span>worker thread中執(zhí)行一個(gè)網(wǎng)頁(yè)的特定腳本。然而,有時(shí),你需要在worker thread中執(zhí)行一個(gè)腳本,并且這個(gè)worker thread對(duì)多個(gè)網(wǎng)頁(yè)通用。在這種情況下,創(chuàng)建許多專(zhuān)用Web Workers,每個(gè)頁(yè)面一個(gè),還不如使用共享Web Workers。由一個(gè)網(wǎng)頁(yè)創(chuàng)建的共享web worker仍然可用于其他網(wǎng)頁(yè)。只有當(dāng)所有到它的連接被關(guān)閉,才能毀壞它。共享Web Workers比專(zhuān)用Web Workers更復(fù)雜一點(diǎn)。
3.Worker對(duì)象代表專(zhuān)用Web Worker
現(xiàn)在,你了解了Web Workers的基礎(chǔ)知識(shí),讓我們看看如何使用專(zhuān)用Web Workers。下面討論的示例假設(shè)你已使用喜歡的開(kāi)發(fā)工具創(chuàng)建了一個(gè)Web應(yīng)用程序,并且還在其Script文件夾中添加了jQuery和Modernizr庫(kù)。將HTML頁(yè)面添加到web應(yīng)用程序,然后鍵入以下代碼:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="scripts/modernizr.js"></script>
<script src="scripts/jquery-2.0.0.js"></script>
<script type="text/javascript"> $(document).ready(function () { if (!Modernizr.webworkers) { alert("This browser doesn't support Web Workers!"); return; } $("#btnStart").click(function () { var worker = new Worker("scripts/lengthytask.js"); worker.addEventListener("message", function (evt) { alert(evt.data); },false); worker.postMessage(10000); }); }); </script>
</head>
<body>
<form>
<input type="button" id="btnStart" value="Start Processing" />
</form>
</body>
</html>
上面的HTML頁(yè)面包含一個(gè)觸發(fā)一些JavaScript處理的按鈕(btnStart)。請(qǐng)注意,該網(wǎng)頁(yè)引用了Modernizr和jQuery庫(kù)。<script>塊包括ready()方法處理程序,并且該處理程序又反過(guò)來(lái)處理btnStart的單擊事件。ready()處理程序首先檢查瀏覽器是否支持web workers。這通過(guò)使用Modernizr的webworkers屬性完成。如果瀏覽器不支持Web workers,則會(huì)向用戶(hù)顯示一條錯(cuò)誤消息。
然后代碼連接btnStart的點(diǎn)擊事件處理程序。點(diǎn)擊事件處理程序的代碼很重要,因?yàn)樗褂肳orker對(duì)象在后臺(tái)運(yùn)行腳本。點(diǎn)擊事件處理程序創(chuàng)建一個(gè)Worker對(duì)象并將其存儲(chǔ)在本地變量——worker中。要在后臺(tái)執(zhí)行的JavaScript文件的路徑在構(gòu)造函數(shù)中傳遞。你將很快創(chuàng)建LengthyTask.js。然后,代碼為Worker對(duì)象的消息事件添加一個(gè)事件處理程序。當(dāng)目標(biāo)腳本(在此情況下為L(zhǎng)engthyTask.js)將一些值發(fā)送回網(wǎng)頁(yè)時(shí),會(huì)引發(fā)消息事件。消息事件處理函數(shù)可以使用evt.data屬性來(lái)訪問(wèn)返回的值。***,在Worker對(duì)象上調(diào)用postMessage()方法來(lái)觸發(fā)LengthyTask.js的執(zhí)行。 postMessage()方法還允許你將數(shù)據(jù)傳遞到目標(biāo)腳本。在此示例中,將一個(gè)數(shù)字(10000)傳遞給postMessage(),postMessage()指示處理應(yīng)持續(xù)的毫秒數(shù)量。你可以傳遞postMessage()調(diào)用中的任何其他數(shù)據(jù),如JavaScript對(duì)象或字符串。
LengthyTask.js文件包含要在后臺(tái)執(zhí)行的代碼,如下所示:
addEventListener("message", function (evt) {
var date = new Date();
var currentDate = null;
do {
currentDate = new Date();
} while (currentDate - date < evt.data);
postMessage(currentDate);
}, false);
上面的代碼處理worker thread的消息事件。當(dāng)主頁(yè)面調(diào)用Worker對(duì)象上的postMessage()方法時(shí),會(huì)引發(fā)消息事件。消息事件處理程序通過(guò)運(yùn)行某些毫秒的do-while循環(huán)來(lái)模擬冗長(zhǎng)的處理。此循環(huán)運(yùn)行的毫秒數(shù)從主頁(yè)傳遞(回憶前面討論的postMessage())。因此,evt.data在此示例中返回10000。一旦長(zhǎng)時(shí)間操作完成,代碼調(diào)用postMessage()會(huì)把處理結(jié)果發(fā)送回主頁(yè)面。在本例中,傳遞currentDate的值(currentDate是一個(gè)Date對(duì)象)。
如果你運(yùn)行主網(wǎng)頁(yè)并單擊Start Processing按鈕,那么你將在10秒后收到alert()。同時(shí),頁(yè)面的用戶(hù)界面不會(huì)被阻止,你可以執(zhí)行諸如滾動(dòng),點(diǎn)擊等操作,表明來(lái)自LengthyTask.js的代碼正在后臺(tái)運(yùn)行。
4. SharedWorker對(duì)象代表共享Web Worker
前面的示例使用了專(zhuān)用Web worker。讓我們將同樣的示例轉(zhuǎn)換為使用共享Web worker。共享Web worker由SharedWorker對(duì)象表示。下面的代碼顯示了來(lái)自主頁(yè)的代碼的修改版本:
$(document).ready(function () {
if (!Modernizr.webworkers)
{
alert("This browser doesn't support Web Workers!");
return;
}
$("#btnStart").click(function () {
var worker = new SharedWorker("scripts/sharedlengthytask.js"); worker.port.addEventListener("message", function (evt) { alert(evt.data); }, false); worker.port.start(); worker.port.postMessage(10000); });
});
注意用粗體字標(biāo)記的代碼。它創(chuàng)建了一個(gè)SharedWorker實(shí)例,并在構(gòu)造函數(shù)中傳遞SharedLengthyTask.js。你將很快創(chuàng)建此文件。然后,代碼將消息事件處理程序連接到SharedWorker對(duì)象的端口對(duì)象。消息處理程序函數(shù)執(zhí)行與前面示例中相同的工作。然后代碼在端口對(duì)象上調(diào)用start()方法。***,在端口對(duì)象上調(diào)用postMessage()方法將數(shù)據(jù)(10000)發(fā)送到共享worker thread。
SharedLengthyTask.js文件包含以下代碼:
var port;
addEventListener("connect", function (evt) {
port = evt.ports[0];
port.addEventListener("message", function (evt) {
var date = new Date();
var currentDate = null;
do {
currentDate = new Date();
} while (currentDate - date < evt.data);
port.postMessage(currentDate);
}, false);
port.start();
}, false);
代碼首先聲明一個(gè)名為port的變量,用于存儲(chǔ)端口對(duì)象的引用。這次處理了兩個(gè)事件——connect和message。當(dāng)與共享Web worker建立連接時(shí),會(huì)觸發(fā)connect事件。 connect事件處理程序捕獲evt.port [0]對(duì)象并將其存儲(chǔ)在之前聲明的端口變量中。然后在端口對(duì)象上連接消息事件處理程序。調(diào)用端口對(duì)象的start()方法來(lái)開(kāi)始偵聽(tīng)該端口上的消息。消息事件處理程序幾乎與你在前面的示例中編寫(xiě)的消息事件處理程序相同,除了它附加到端口對(duì)象這一點(diǎn)。此外,在端口對(duì)象上調(diào)用postMessage(),以將處理結(jié)果發(fā)送到主頁(yè)面。
5. Web Workers可以使用XMLHttpRequest與服務(wù)器通信
有時(shí)Web Worker可能需要與Web服務(wù)器通信。例如,你可能需要駐留在某些RDBMS中的數(shù)據(jù)以便于客戶(hù)端處理。要完成此類(lèi)任務(wù),你可以使用XMLHttpRequest對(duì)象向服務(wù)器端資源發(fā)出請(qǐng)求。實(shí)例化Worker對(duì)象和處理消息事件的整個(gè)過(guò)程保持不變。但是,你需要向服務(wù)器端資源發(fā)出GET或POST請(qǐng)求。考慮下面的代碼:
addEventListener("message", function (evt) {
var xhr = new XMLHttpRequest(); xhr.open("GET", "lengthytaskhandler.ashx"); xhr.onload = function () { postMessage(xhr.responseText); }; xhr.send(); }, false);
上面顯示的代碼創(chuàng)建了XMLHttpRequest對(duì)象的實(shí)例。然后調(diào)用open()方法,并指定向服務(wù)器端資源LengthyTaskHandler.ashx(一個(gè)ASP.NET通用處理程序)發(fā)出GET請(qǐng)求。(雖然此示例使用ASP.NET通用處理程序,但你可以使用任何其他服務(wù)器端資源。)然后它處理XMLHttpRequest對(duì)象的load事件并調(diào)用postMessage()。 xhr.responseText作為postMessage()的參數(shù)。xhr.responseText將是ASP.NET通用處理程序作為響應(yīng)返回的值。請(qǐng)求完成時(shí)引發(fā)load事件。
LengthyTaskHandler.ashx包含以下代碼:
namespace WebWorkersDemo
{
public class LengthyTaskHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context) { System.Threading.Thread.Sleep(10000); context.Response.ContentType = "text/plain"; context.Response.Write("Processing successful!"); }
public bool IsReusable
{
get
{
return false;
}
}
}
}
正如你可以看到,ProcessRequest()通過(guò)在Thread類(lèi)上調(diào)用Sleep()方法來(lái)模擬一些冗長(zhǎng)的處理,并阻止執(zhí)行10秒。然后它返回一個(gè)成功消息“Processing successful!”給調(diào)用者。如果你在進(jìn)行這些更改后運(yùn)行主網(wǎng)頁(yè),你會(huì)發(fā)現(xiàn)在10秒后,將顯示一個(gè)包含此成功消息的警報(bào)對(duì)話(huà)框。
6.你可以使用錯(cuò)誤事件捕獲未處理的錯(cuò)誤
如果你的Web worker正在進(jìn)行一些復(fù)雜的操作,那么你可能需要添加錯(cuò)誤處理到主網(wǎng)頁(yè)代碼,以便在worker中出現(xiàn)任何未處理錯(cuò)誤時(shí),可以采取適當(dāng)?shù)牟僮鳌_@可以通過(guò)處理Worker對(duì)象的錯(cuò)誤事件來(lái)完成。每當(dāng)work thread中存在任何未處理的錯(cuò)誤時(shí),就會(huì)拋出錯(cuò)誤事件。以下代碼顯示了如何完成此操作:
$("#btnStart").click(function () {
var worker = new Worker("scripts/lengthytask.js");
worker.addEventListener("message", function (evt) {
alert(evt.data);
}, false);
worker.addEventListener("error", function (evt) { alert("Line #" + evt.lineno + " - " + evt.message + " in " + evt.filename); }, false); worker.postMessage(10000);
});
從上面的代碼可以看出,錯(cuò)誤處理程序已經(jīng)連接到worker對(duì)象的錯(cuò)誤事件。錯(cuò)誤處理函數(shù)接收一個(gè)事件對(duì)象,而該對(duì)象提供錯(cuò)誤信息,例如發(fā)生錯(cuò)誤的行號(hào)(evt.lineno),錯(cuò)誤消息(evt.message)和發(fā)生錯(cuò)誤的文件(evt.filename)。
7.你可以使用Terminate()方法終止worker
有時(shí)你可能會(huì)想要取消worker中正在執(zhí)行的任務(wù)。對(duì)此,你可以通過(guò)調(diào)用其terminate()方法來(lái)摧毀Worker。一旦Worker終止,你就不能重新使用或重新啟動(dòng)它。當(dāng)然,你總是可以創(chuàng)建另一個(gè)Worker實(shí)例并使用它。但請(qǐng)記住,terminate()會(huì)立即殺死了worker,并且不會(huì)給你任何機(jī)執(zhí)行清理操作。
總結(jié)
Web workers允許你在后臺(tái)執(zhí)行腳本而不凍結(jié)網(wǎng)頁(yè)用戶(hù)界面。有兩種類(lèi)型——專(zhuān)用web worker和共享web worker。每個(gè)網(wǎng)頁(yè)創(chuàng)建專(zhuān)用web worker,而跨多個(gè)網(wǎng)頁(yè)使用共享web worker共享。Worker類(lèi)代表專(zhuān)用web worker,SharedWorker類(lèi)代表共享web worker。本文介紹了如何使用這兩種類(lèi)型,文中還討論了如何處理錯(cuò)誤以及webworker如何使用XMLHttpRequest與web服務(wù)器通信。
譯文鏈接:http://www.codeceo.com/article/7-things-about-web-workers.html
英文原文:7 Things You Need To Know About Web Workers