在今天以前所介紹的都是一般較常用到的 Dedicated worker,Dedicated worker 只能被當下創建的線程使用,而 Shared worker 則是只要符合同源的網域下,任意不同的 window, iframe 或甚至是其他的 worker 線程都可以使用它。
Shared worker 藉由 第 10 天介紹到的 MessagePort 達到不同線程之間的溝通,所以需要使用 port 對象傳遞訊息
// 主線程
const worker = new SharedWorker('worker.js');
worker.port.postMessage(1);
不同於 Dedicated worker 只需要使用 onmessage 接收訊息,Shared worker 需要在 worker 連接上的時候先呼叫 onconnect 方法,取得連接到 worker 的 port 後,再使用 port 去接收或送出訊息
另外使用 port.addEventListener 或是 port.onmessage 接收訊息時有個小地方需要注意, port.addEventListener 的寫法接收訊息,一定要呼叫 port.start(),才有辦法順利收到訊息,但使用 port.onmessage 方式的寫法,底層會自動呼叫 start(),就不用特別寫出來
例如,以下兩段程式碼是等價的:
self.onconnect = (e) => {
const port = e.ports[0];
port.addEventListener('message', (e) => {
port.postMessage(e.data);
});
port.start();
};
self.onconnect = (e) => {
const port = e.ports[0];
port.onmessage = (e) => {
port.postMessage(e.data);
};
};
跟上面提到的一樣,當使用 addEventListener('message') 的寫法時,port 連線不會自動建立,需要呼叫 port.start() 才能正確收到訊息
worker.port.onmessage = (e) => {
console.log(e.data);
};
worker.port.addEventListener('message', (e) => {
console.log(e.data);
});
worker.port.start();
Dedicated worker 每次創建時都會是一個新的 worker,只能被創建當下的線程所使用,但 Shared worker 不論被創建了多少次,所有頁面(tab) 都是共用同一個 worker,以下表格統整 不同事件 所產生的 總 worker 數量
Dedicated worker
const worker = new Worker('/worker.js');
| 事件 | 結果 | 總共 worker 數量 |
|---|---|---|
| tab1 創建 worker | 產生 worker1 | 1 |
| tab2 創建 worker | 產生 worker2 | 2 |
| 關閉 tab1 | 終止 worker1 | 1 |
| 關閉 tab2 | 終止 worker2 | 0 |
Shared worker
const worker = new SharedWorker('/shared-worker.js');
| 事件 | 結果 | 總共 worker 數量 |
|---|---|---|
| tab1 創建 shared worker | 產生 shared worker1 | 1 |
| tab2 創建 shared worker | 產生 shared worker2 | 1 |
| 關閉 tab1 | 終止 shared worker1 | 1 |
| 關閉 tab2 | 終止 shared worker2 | 0 |
只要是同源的網域下都可以共用同一個 Shared worker,因此 Shared worker 的生命週期也與 Dedicated worker 不同,不論 Shared worker 被創建了幾次,指向的都是同一個 Shared worker
SharedWorker
The secret of successfully using multi window WebGL Canvas
JavaScript Web Workers(三)共享Worker