關於 Web Workers 你需要了解的 7 件事

2017-05-06    分類:WEB開發、程式設計開發、首頁精華0人評論發表於2017-05-06

本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

介紹

Web Workers允許你在後臺執行JavaScript程式碼,而不會阻止web使用者介面。Web Workers可以提高網頁的整體效能,還可以增強使用者體驗。Web Workers有兩種風格 ——專用Web Workers和共享Web Workers。本文討論了你所需要知道的Web worker的七個關鍵方面,幫助你決定在應用程式中使用它們的話。

1.Web Workers允許你在後臺執行JavaScript程式碼

通常,你在Web頁面中編寫的JavaScript程式碼在與使用者介面相同的執行緒中執行。這就是為什麼當你點選一個會觸發漫長處理過程的按鈕,網頁的使用者介面會凍結。除非處理完成,否則你就無法工作於使用者介面。Web worker允許你在後臺執行JavaScript,以便使用者介面保持響應,即使同時正在執行某些指令碼。執行指令碼的後臺執行緒通常稱為worker thread或worker。你可以生成儘可能多的worker,只要你想。你還可以將資料傳遞到正在worker thread中執行的指令碼,並在完成時將值返回到主執行緒。然而,Web Workers有一些限制,如下所示:

  • Web Workers無法從web頁面訪問DOM元素。
  • Web Workers無法從web頁面訪問全域性變數和JavaScript函式。
  • Web Workers不能呼叫alert()或confirm()函式。
  • 不能在Web Workers中訪問諸如視窗,文檔和parent這樣的對象。

但是,你可以使用setTimeout(),setInterval()等函式。你也可以使用XMLHttpRequest物件向伺服器發出Ajax請求。

2.Web Workers有兩種型別

Web Workers有兩種型別:專用Web Workers和共享Web Workers。專用Web Workers隨同建立它們的網頁一起存在和死亡。這意味著在網頁中建立的專用Web Workers無法通過多個網頁訪問。另一方面,共享Web Workers在多個網頁之間是共享的。Worker類代表專用Web Workers,而SharedWorker類代表共享Web Workers。

在許多情況下,專用Web Workers就可以滿足你的需求。這是因為通常你需要在worker thread中執行一個網頁的特定指令碼。然而,有時,你需要在worker thread中執行一個指令碼,並且這個worker thread對多個網頁通用。在這種情況下,建立許多專用Web Workers,每個頁面一個,還不如使用共享Web Workers。由一個網頁建立的共享web worker仍然可用於其他網頁。只有當所有到它的連線被關閉,才能毀壞它。共享Web Workers比專用Web Workers更復雜一點。

3.Worker物件代表專用Web Worker

現在,你瞭解了Web Workers的基礎知識,讓我們看看如何使用專用Web Workers。下面討論的示例假設你已使用喜歡的開發工具建立了一個Web應用程式,並且還在其Script資料夾中新增了jQuery和Modernizr庫。將HTML頁面新增到web應用程式,然後鍵入以下程式碼:

<!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頁面包含一個觸發一些JavaScript處理的按鈕(btnStart)。請注意,該網頁引用了Modernizr和jQuery庫。<script>塊包括ready()方法處理程式,並且該處理程式又反過來處理btnStart的單擊事件。ready()處理程式首先檢查瀏覽器是否支援web workers。這通過使用Modernizr的webworkers屬性完成。如果瀏覽器不支援Web workers,則會向使用者顯示一條錯誤訊息。

然後程式碼連線btnStart的點選事件處理程式。點選事件處理程式的程式碼很重要,因為它使用Worker物件在後臺執行指令碼。點選事件處理程式建立一個Worker物件並將其儲存在本地變數——worker中。要在後臺執行的JavaScript檔案的路徑在建構函式中傳遞。你將很快建立LengthyTask.js。然後,程式碼為Worker物件的訊息事件新增一個事件處理程式。當目標指令碼(在此情況下為LengthyTask.js)將一些值傳送回網頁時,會引發訊息事件。訊息事件處理函式可以使用evt.data屬性來訪問返回的值。最後,在Worker物件上呼叫postMessage()方法來觸發LengthyTask.js的執行。 postMessage()方法還允許你將資料傳遞到目標指令碼。在此示例中,將一個數字(10000)傳遞給postMessage(),postMessage()指示處理應持續的毫秒數量。你可以傳遞postMessage()呼叫中的任何其他資料,如JavaScript物件或字串。

LengthyTask.js檔案包含要在後臺執行的程式碼,如下所示:

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的訊息事件。當主頁面呼叫Worker物件上的postMessage()方法時,會引發訊息事件。訊息事件處理程式通過執行某些毫秒的do-while迴圈來模擬冗長的處理。此迴圈執行的毫秒數從主頁傳遞(回憶前面討論的postMessage())。因此,evt.data在此示例中返回10000。一旦長時間操作完成,程式碼呼叫postMessage()會把處理結果傳送回主頁面。在本例中,傳遞currentDate的值(currentDate是一個Date物件)。

如果你執行主網頁並單擊Start Processing按鈕,那麼你將在10秒後收到alert()。同時,頁面的使用者介面不會被阻止,你可以執行諸如滾動,點選等操作,表明來自LengthyTask.js的程式碼正在後臺執行。

4. SharedWorker物件代表共享Web Worker

前面的示例使用了專用Web worker。讓我們將同樣的示例轉換為使用共享Web worker。共享Web worker由SharedWorker物件表示。下面的程式碼顯示了來自主頁的程式碼的修改版本:

$(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);   });
});

注意用粗體字標記的程式碼。它建立了一個SharedWorker例項,並在建構函式中傳遞SharedLengthyTask.js。你將很快建立此檔案。然後,程式碼將訊息事件處理程式連線到SharedWorker物件的埠物件。訊息處理程式函式執行與前面示例中相同的工作。然後程式碼在埠物件上呼叫start()方法。最後,在埠物件上呼叫postMessage()方法將資料(10000)傳送到共享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);

程式碼首先宣告一個名為port的變數,用於儲存埠物件的引用。這次處理了兩個事件——connect和message。當與共享Web worker建立連線時,會觸發connect事件。 connect事件處理程式捕獲evt.port [0]物件並將其儲存在之前宣告的埠變數中。然後在埠物件上連線訊息事件處理程式。呼叫埠物件的start()方法來開始偵聽該埠上的訊息。訊息事件處理程式幾乎與你在前面的示例中編寫的訊息事件處理程式相同,除了它附加到埠物件這一點。此外,在埠物件上呼叫postMessage(),以將處理結果傳送到主頁面。

5. Web Workers可以使用XMLHttpRequest與伺服器通訊

有時Web Worker可能需要與Web伺服器通訊。例如,你可能需要駐留在某些RDBMS中的資料以便於客戶端處理。要完成此類任務,你可以使用XMLHttpRequest物件向伺服器端資源發出請求。例項化Worker物件和處理訊息事件的整個過程保持不變。但是,你需要向伺服器端資源發出GET或POST請求。考慮下面的程式碼:

addEventListener("message", function (evt) {
 var xhr = new XMLHttpRequest(); xhr.open("GET", "lengthytaskhandler.ashx"); xhr.onload = function () { postMessage(xhr.responseText); }; xhr.send(); }, false);

上面顯示的程式碼建立了XMLHttpRequest物件的例項。然後呼叫open()方法,並指定向伺服器端資源LengthyTaskHandler.ashx(一個ASP.NET通用處理程式)發出GET請求。(雖然此示例使用ASP.NET通用處理程式,但你可以使用任何其他伺服器端資源。)然後它處理XMLHttpRequest物件的load事件並呼叫postMessage()。 xhr.responseText作為postMessage()的引數。xhr.responseText將是ASP.NET通用處理程式作為響應返回的值。請求完成時引發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()通過在Thread類上呼叫Sleep()方法來模擬一些冗長的處理,並阻止執行10秒。然後它返回一個成功訊息“Processing successful!”給呼叫者。如果你在進行這些更改後執行主網頁,你會發現在10秒後,將顯示一個包含此成功訊息的警報對話方塊。

6.你可以使用錯誤事件捕獲未處理的錯誤

如果你的Web worker正在進行一些複雜的操作,那麼你可能需要新增錯誤處理到主網頁程式碼,以便在worker中出現任何未處理錯誤時,可以採取適當的操作。這可以通過處理Worker物件的錯誤事件來完成。每當work thread中存在任何未處理的錯誤時,就會丟擲錯誤事件。以下程式碼顯示瞭如何完成此操作:

$("#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);
});

從上面的程式碼可以看出,錯誤處理程式已經連線到worker物件的錯誤事件。錯誤處理函式接收一個事件物件,而該物件提供錯誤資訊,例如發生錯誤的行號(evt.lineno),錯誤訊息(evt.message)和發生錯誤的檔案(evt.filename)。

7.你可以使用Terminate()方法終止worker

有時你可能會想要取消worker中正在執行的任務。對此,你可以通過呼叫其terminate()方法來摧毀Worker。一旦Worker終止,你就不能重新使用或重新啟動它。當然,你總是可以建立另一個Worker例項並使用它。但請記住,terminate()會立即殺死了worker,並且不會給你任何機執行清理操作。

總結

Web workers允許你在後臺執行指令碼而不凍結網頁使用者介面。有兩種型別——專用web worker和共享web worker。每個網頁建立專用web worker,而跨多個網頁使用共享web worker共享。Worker類代表專用web worker,SharedWorker類代表共享web worker。本文介紹瞭如何使用這兩種型別,文中還討論瞭如何處理錯誤以及webworker如何使用XMLHttpRequest與web伺服器通訊。

譯文連結:http://www.codeceo.com/article/7-things-about-web-workers.html
英文原文:7 Things You Need To Know About Web Workers
翻譯作者:碼農網 – 小峰
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章