Message Channel

Message Channel 為 Browser 提供的 API,主要會有 port1 跟 port2, 對 port1 發送訊息會傳送到 port2,反之亦然。

Message Channel 其目的在於對於不同的執行環境 (主執行緒, Web Worker, Service Worker, Iframe or Tabs ) 之間,建立一個直接且專屬專用的通訊管道,可以讓不同 worker 之間的通訊程式從主執行緒中解耦藉此來提高效能。

範例

MDN Sandbox

<!-- index.html -->
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width" />
    <title>Channel messaging demo</title>
    <style>
      iframe {
        border-radius: 1em;
      }
    </style>
  </head>
  <body>
    <h1>Channel messaging demo</h1>
    <p class="output">Index.html para (I will be overwritten)</p>
    <iframe src="page2.html" width="480" height="320"></iframe>
    <script>
      const channel = new MessageChannel();
      const output = document.querySelector(".output");
      const iframe = document.querySelector("iframe");
 
      // Wait for the iframe to load
      iframe.addEventListener("load", onLoad);
 
      function onLoad() {
        // Listen for messages on port1
        channel.port1.onmessage = onMessage;
        // Transfer port2 to the iframe
        iframe.contentWindow.postMessage(
          "A message from the index.html page!",
          "*",
          [channel.port2]
        );
      }
 
      // Handle messages received on port1
      function onMessage(e) {
        output.innerText = e.data;
      }
    </script>
  </body>
</html>
 
<!-- page2.html -->
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width" />
    <title>Page 2</title>
    <style>
      body {
        background-color: aliceblue;
        font-family: monospace;
      }
    </style>
  </head>
  <body>
    <p class="output">page2.html (iframe body)</p>
    <script>
      const output = document.querySelector(".output");
 
      window.addEventListener("message", onMessage);
 
      function onMessage(e) {
        if (!e.ports.length) return;
 
        output.innerText = e.data;
        // Use the transferred port to post a message to the main frame
        e.ports[0].postMessage("A message from the iframe in page2.html");
      }
    </script>
  </body>
</html>
 

Summary By AI

MessageChannel 主要解決了在不同的 JavaScript 執行環境(像是主執行緒、Web Workers、Service Workers,甚至 iframe 或使用 window.open 開啟的視窗之間)建立直接、專用、雙向通訊管道的需求。

以下是需要 MessageChannel 的幾個主要原因:

  1. 實現 Worker 之間的直接通訊 (最重要的原因之一):

    • 問題: 如果你有兩個或多個 Web Workers 需要互相溝通,預設情況下它們無法直接對話。它們的訊息通常需要先發回主執行緒,再由主執行緒轉發給目標 Worker。

    • 解決方案: 使用 MessageChannel。主執行緒可以建立一個 MessageChannel,然後將 port1 傳遞給 Worker A,將 port2 傳遞給 Worker B。之後,Worker A 和 Worker B 就可以使用各自收到的 port 直接互相發送和接收訊息,完全繞過主執行緒

    • 優點:

      • 效能提升: 減少了訊息傳遞的延遲。

      • 減輕主執行緒負擔: 將通訊的負載從主執行緒(負責 UI 渲染和使用者互動)轉移開,避免主執行緒成為瓶頸,提升應用程式的響應速度。

  2. 建立專用的通訊管道 (即使是主執行緒與 Worker 之間):

    • 問題: 主執行緒和 Worker 之間雖然有預設的 postMessage/onmessage 機制,但所有的通訊都混雜在一起。如果你希望針對不同類型的任務或數據流使用不同的處理邏輯,只用預設通道會比較混亂。

    • 解決方案: 你可以建立多個 MessageChannel,並將其中一個 port 傳遞給 Worker。例如,一個 Channel 用於發送控制指令,另一個用於傳輸大量數據。

    • 優點:

      • 更好的程式碼組織: 可以為不同的 port 設定不同的 onmessage 處理器,讓邏輯更清晰。

      • 區分優先級或類型: 可以更容易地區分和處理不同性質的通訊。

  3. 提供一次性的、所有權轉移的通訊端點:

    • MessageChannel 的 port 是**可轉移物件 (Transferable Objects)**。當你透過 postMessage 的第二個參數將 port 轉移給另一個執行緒時,原始執行緒就失去了對該 port 的控制權。

    • 優點:

      • 明確所有權: 清楚地表明哪個執行緒負責該通訊端點。

      • 效能 (間接): 雖然 port 本身不大,但轉移機制是 Web 平台設計中用於高效傳遞資源(尤其是大型資源如 ArrayBuffer)的方式,MessageChannel 遵循了這種高效模式。

  4. 實現更複雜的通訊模式:

    • 有了 MessageChannel,你可以更容易地構建更複雜的架構,例如:

      • 管道 (Pipeline): 數據在多個 Worker 之間依次處理。

      • 扇出/扇入 (Fan-out/Fan-in): 一個 Worker 將任務分發給多個 Worker,然後收集結果。

總結來說,MessageChannel 提供了一種標準、高效且靈活的方式,用於在不同的 JavaScript 執行上下文之間建立直接、專用的通訊連結,特別是在需要 Worker 之間直接通訊或需要將通訊從主執行緒解耦以提高效能和響應性時,它就顯得非常有用。 它克服了預設 postMessage 機制的某些限制,使得更複雜、更高效的併發程式設計成為可能。