Get to know MDN better
此頁面由社群從英文翻譯而來。了解更多並加入 MDN Web Docs 社群。
Web Worker 提供簡單的方法讓網頁在背景執行緒(Thread)中執行程式,而不干擾使用者介面運行,另外,Worker 也可以利用 XMLHttpRequest 執行輸出/輸入(但是 responseXML 和 channel 這兩個屬性為 null);一個 worker 可以藉由事件處理器來和 web worker 創造端互相傳送訊息,接下來本文會提供使用 web worker 的詳細說明。
透過 worker 建構子 (如 Worker()) 便可以產生 worker 物件,並且執行 JavaScript 檔案。在 worker 中的 JavaScript 運行在不同於 window 的執行緒環境,所以在 worker 中存取全域物件應該要透過 self,如果透過 window 會導致錯誤發生。
Dedicated worker (專有 worker) 是一般 worker,只能被產生它的檔案存取,DedicatedWorkerGlobalScope 物件代表其執行環境;而 Shared worker (共享 worker) 則能夠被不同檔案存取,SharedWorkerGlobalScope) 物件代表其執行環境。
備註:worker 其他文件說明請見 The Web Workers API landing page 。
基本上 worker 能夠執行任何事情,比如說 WebSockets、IndexedDB、和 Firefox OS 特有的 Data Store API ,然而直接存取 DOM 或是 window 物件的一些方法和屬性則不被允許,更多細節請見 worker 可存取知函數和類別。
主執行緒和 worker 執行緒之間用 postMessage() 方法發送訊息,然後透過 onmessage 事件接受訊息 (訊息存在 message 事件的 data 屬性之中),其中被傳送的資料並非共享而是複製一份後傳送。
worker 可以產生新 worker,只要新 worker 的來源 (origin) 和父頁面相同,也可以利用 XMLHttpRequest 執行輸出/輸入(但是 responseXML 和 channel 這兩個屬性為 null)。
dedicated worker 只能被產生它的檔案存取,下面我們先介紹簡單的 Basic dedicated worker example (run dedicated worker) 範例。這個範例會將兩個數字送入 worker 相乘,然後再於前端頁面顯示相乘結果。
為了向下相容、避免錯誤,最好是確保 worker 存在後再取用之(main.js):
只要呼叫 Worker() 建構子,傳入 JS 檔案的 URI,便可以生成一個 worker 執行緒(main.js):
postMessage() 方法以及 onmessage 事件處理器就是和 worker 發送訊息的關鍵(main.js):
範例中有兩個 <input> 元素,first 和 second,當元素值改變時,我們會利用 postMessage() 方法告訴 worker 改變的值 (這邊用陣列,也可以用其他類別)。
然後在 worker 裡我們從 onmessage 接收訊息(worker.js):
onmessage 事件物件的 data 屬性存有傳送過來的訊息資料,也就是 input 值;worker 收到後將傳過來的兩個值相乘,再 postMessage 傳回去。
回到主執行,同樣透過 onmessage 事件,收到 worker 回傳還來的計算值 :
拿到存在事件 data 中的計算值後,我們接著將值以 textContent 顯示出來。
備註:建構 Worker 的 URI 必須遵從同源政策。目前各家瀏覽器在這方面存有歧異,Gecko 10.0 以後允許 data URI 而 Internet Explorer 10 不允許 Blob URI。
備註:在主執行緒中存取 onmessage 與 postMessage 需要主動掛在 worker 物件上,在 worker 執行緒則不用,這是因為 worker 執行緒的全域物件便是 worker 物件。
備註:和 worker 傳送的資料並非共享而是複製一份後傳送,詳細請參照 和 workers 傳遞資料:更多細節。
在主執行緒裡呼叫 terminate 就可結束 worker:
請注意不論 worker 正在執行的運算完成與否,一但呼叫後 worker 便會立刻被終止。
而在 worker 執行緒裡,worker 可以呼叫自己的 close 方法來結束 :
當執行時期錯誤發生時,onerror 事件處理器會被呼叫,onerror 事件處理器會收到一名為 error 的事件物件 (實作 ErrorEvent Interface),該事件不會 bubble 且可取消,如果要避免事件預設行為,可以呼叫 preventDefault()。
以下三個部分是錯誤事件較關鍵的地方:
message供人閱讀的錯誤訊息
filename錯誤發生所在的檔案名稱
lineno錯誤發生所在的行數
worker 可以產生其他 worker (subworker),subworker 的來源也必須和主頁相同,另外,subworker 的 URI 的解析是相對於父 worker 的位置而非所在頁面,這項特色有助於追蹤 worker 間的相依性。
Worker 執行緒能存取一個全域函數 (global function), importScripts()。importScripts() 可以讓 worker 端引入相同網域的程式碼腳本與 libraries,importScripts()可接收零到數個要被輸入資源的 URI,底下為幾個範例:
瀏覽器會載入並執行每個程式碼腳本,然後 worker 能夠存取程式碼腳本內定義的全域變數,若是腳本無法載入,會產生一個 NETWORK_ERROR,後續的程式碼不會被執行,但是先前執行過的程式碼或用 window.setTimeout() 延遲執行的程式碼依然有效,而 importScripts() 之後宣告的函數也一樣存在,因為這些程式碼總是在其他程式碼之前就解析過了。
備註:雖然程式碼腳本的下載順序不一定,但執行順序會遵照傳入 importScripts()的順序,這是同步完成的,importScripts()不會回傳直到所有的程式碼都下載並執行完。
shared worker 能夠被多個程式腳本存取,縱使跨越不同 window、iframe 或 worker。這邊的 Basic shared worker example (run shared worker) 範例和 dedicated worker 範例類似,但多了兩個可以讓多個檔案存取的函數:數字相乘以及數字平方。
請注意 dedicated worker 與 shared worker 間的差異處,範例裡會有兩份 HTML 頁面,各自都利用同一個 worker 處理運算。
備註:所有的瀏覽環境都必需共享相同的來源(相同 protocol, host 和 port),shared worker 才能讓不同瀏覽環境存取。
備註:在 Firefox,shared worker 無法在一般和隱私模式間共享(Firefox bug 1177621)。
和 dedicated worker 做法差不多,只是用另一個 SharedWorker 建構子來產生 shared worker,見 index.html 和 index2.html:
相當不 一樣的是和 shared worker 溝通必須要透過 port 物件,其實 dedicated worker 也是如此,只不過一切是在背景後自動完成。
開啟 port 連線一是在 onmessage 事件下背景完成,二是藉由主動呼叫 start() 好開始傳送訊息。範例 multiply.js 以及 worker.js 因為註冊了 onmessage 事件,所以其實可以省略呼叫 start(),然而若是 message 事件是經由 addEventListener() 註冊,那麼便需要呼叫 start() 了。
當使用 start() 開啟 port 連線,那麼雙向溝通便需要主執行緒和 worker 兩端都呼叫 start()。
如同前面,現在可以呼叫 postMessage() 發送訊息,只不過這次需要透過 port 物件(一樣請參考 multiply.js 和 square.js):
worker 方面也增加了一些程式碼(worker.js):
首先,先監聽連線建立的 onconnect 事件,例如當主執行緒建立 onmessage 事件或呼叫 start()。
然後從 onconnect 事件物件,我們可以取得 port 物件使用之。
取得 port 之後,我們註冊 port 上的 onmessage 事件,當有訊息進來便取回資料進行運算後回傳回去;註冊 onmessage 事件的同時也自動建立連線,所以說不需要呼叫 start() 了。
最後在主執行緒端,我們同樣由 onmessage 事件取回回傳過來的訊息(一樣請參考 multiply.js 和 square.js):
Worker 會產生真正 OS 層級的執行緒,細心的開發者或許會擔心同步問題。
不過 worker 會十分注意和其他執行緒溝通的狀況,不會去存取非執行緒安全的元件,如 DOM ,而且資料的傳遞也都序列化 (serialized) ,所以說很難會發生同步問題。
和 workers 傳遞的資料會先被複製一份,而非共享;經過序列化後 (serialized) 傳輸,然後在另一端反序列化 (de-serialized) 取出,大部份的瀏覽器都是以 結構化複製 (structured cloning) 實作這項特色.
下面的 emulateMessage() 會模擬和 worker 傳遞訊息時,複製資料的行為。
所謂的訊息就是經過複製、非共享的資料,到這邊你應該已經知道 postMessage() 負責發送訊息,然後 message 事件 data 的 attribute 則存有傳送的訊息資料。
example.html: (the main page):
my_task.js (the worker):
結構化複製(structured cloning) 演算法支援 JSON 以及迴圈參照(circular references)。
下面透過 data URL 和 eval(),示範如何在 worker 非同步執行允許的程式碼:
data URL 相當於網路請求,範例中的 data URL 會在 worker 執行下列程式碼回應訊息:
應用範例:
下面的範例系統適合需要在主頁面和 worker 傳遞複雜資料和呼叫多個函數的情境。
example.html (主頁面):
my_task.js (worker):
Google Chrome 17+ 以及 Firefox 18+ 能夠和 worker 高效能地傳送另外一種特定型態物件 (可移轉物件, transferable objects,這種物件實作了 Transferable 介面),可移轉物件當被傳送到另一端時並不需要複製,因此可以大大提升傳送大型資料物件的效能;這好比像是 C/C++ 的 pass-by-reference,但是不同的是,一旦移轉後原先的環境便失去了持有資料,例如當主頁面傳送 ArrayBuffer 後,主頁面便不再能夠使用這筆資料物件了,這筆資料物件的存取連結已經靜靜地移轉到 worker 端了。
備註:關於更多可移轉物件的資訊, 效能和功能偵測,請參考 HTML5 Rocks 上 Transferable Objects: Lightning Fast! 一文。
不像 <script>,並沒有一套正式標準的方法將 worker 的程式碼嵌入到頁面之中,不過沒有 src 屬性而且 mime-type 不屬於可執行程式碼的 <script> 元素會被視為 javascript 可以取用的資料區塊(data block),資料區塊是一項 HTML5 可用於攜帶文字資料的特色功能,利用資料區塊我們就有辦法嵌入 worker 的程式碼到頁面中:
Embedded worker 在 document.worker 之中。
下面介紹其他使用 worker 的範例。
worker 主要的用處在避免重度 CPU 運算的任務阻礙到 UI 執行緒運行;這邊我們用 worker 來跑 Fibonacci 數列運算。
fibonacci.js 中的程式碼會被另一份 HTML 引用。
worker 程式碼中註冊了一個 onmessage 事件處理器用來接收另一端 postMessage 過來的訊息 (請注意這並非定義一個全域變數或函數,var onmessage 或 function onmessage 會定義全域變數,但不會註冊事件處理器),然後開始進行遞迴運算。
onmessage 事件處理器會接收 worker 回傳的運算結果,然後顯示在頁面上,如果有問題, onerror 事件處理器會 輸出 錯誤訊息。
和 worker 溝通則是利用 postMessage。
範例測試。
範例請見 Using workers in extensions 。
基於多核 cpu 的普及,分割複雜任務到多個 workers 將可能有助於利用多核心 cpu 的優勢。
除了 dedicated 和 shared web workers,還有其他種類:
大多數 Javascript 的功能 worker 皆可以使用,包含:
worker 無法操作主頁面的物件與 DOM,如有相關需求,必須要間接透過 DedicatedWorkerGlobalScope.postMessage 通知主頁面,讓主頁面執行需求。
備註:所有 worker 可存取功能一覽表,請見 Functions and interfaces available to workers.
| HTML # workers |
This page was last modified on 2025年12月2日 by MDN contributors.
Your blueprint for a better internet.
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998–2026 by individual mozilla.org contributors. Content available under a Creative Commons license.