一、初識 MessageChannel 對象
通過構造函數 MessageChannel() 可以創建一個消息通道,實例化的對象會繼承兩個屬性:port1 和 port2

port1 和 port2 都是 MessagePort 對象,在這里是只讀的,無法對其進行字面量賦值

不過可以給 port 添加屬性

上圖還體現了 MessagePort 對象具有 onmessage 和 onmessageerror 兩個屬性
這是兩個回調方法,使用 MessagePort.postMessage 方法發送消息的時候,就會觸發另一個端口的 onmessage
消息通道就像是一條左右貫通的管道,左右兩個端口就是 port1 和 port2
這兩個端口可以相互發送消息,port1 發送的消息可以在 port2 接收到,反之亦然

二、多個 Web Worker 之間通信
MessageChannel 可以結合 Web Worker 實現多線程通信
// main.js
let worker1 = new Worker('./worker1.js'); let worker2 = new Worker('./worker2.js'); let ms = new MessageChannel();
// 把 port1 分配給 worker1 worker1.postMessage('main', [ms.port1]);
// 把 port2 分配給 worker2 worker2.postMessage('main', [ms.port2]); worker2.onmessage = function(event) { console.log(event.data); }
這里的 postMessage() 可以接收兩個參數:message、transferList
| message | 消息內容,可以是任意基礎數據類型 |
| transferList | 由被傳輸對象組成的數組,這些對象的所有權會轉移給調用 postMessage 的對象 |
所以上面的代碼,就是把消息通道的 port1 分配給了 worker1,把 port2 分配給 worker2
也就是用消息通道,將兩個 worker 給連接起來
// worker1.js onmessage = function(e) { if (e.data === 'main') { const port = e.ports[0]; port.postMessage('Hi! I'm worker1'); } }
// worker2.js
onmessage = function(e) { if (e.data === 'main') { const port = e.ports[0]; port.onmessage = function(e) { postMessage(e.data); } } }
代碼運行的時候,worker1 中通過 port1 發送消息,然后 worker2 就能從 port2 中接收到消息
三、深拷貝
大部分需要深拷貝的場景,都可以使用以下代碼:
JSON.parse(JSON.stringify(object))
但這種辦法會忽略 undefined、function、symbol 和循環引用的對象
而通過 postMessage() 方法傳輸的 message 參數是深拷貝的
所以可以借用 MessageChannel 實現深拷貝:
// 深拷貝函數
function deepClone(val) { return new Promise(resolve => { const { port1, port2 } = new MessageChannel() port2.onmessage = e => resolve(e.data) port1.postMessage(val) }) } // 定義一個包含 undefined 的對象
let obj = { a: 'wise', b: undefined, c: { d: 'wrong' } } // 循環引用
obj.c.e = obj.c // 注意該方法是異步的
async function test() { const clone = await deepClone(obj) console.log(clone) } test()
但這個深拷貝只能解決 undefined 和循環引用對象的問題,對於 Symbol 和 function 依然束手無策
參考資料:
