Message Channel
Message Channel 為 Browser 提供的 API,主要會有 port1 跟 port2, 對 port1 發送訊息會傳送到 port2,反之亦然。
Message Channel 其目的在於對於不同的執行環境 (主執行緒, Web Worker, Service Worker, Iframe or Tabs ) 之間,建立一個直接且專屬專用的通訊管道,可以讓不同 worker 之間的通訊程式從主執行緒中解耦藉此來提高效能。
範例
<!-- 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 的幾個主要原因:
-
實現 Worker 之間的直接通訊 (最重要的原因之一):
-
問題: 如果你有兩個或多個 Web Workers 需要互相溝通,預設情況下它們無法直接對話。它們的訊息通常需要先發回主執行緒,再由主執行緒轉發給目標 Worker。
-
解決方案: 使用
MessageChannel。主執行緒可以建立一個MessageChannel,然後將port1傳遞給 Worker A,將port2傳遞給 Worker B。之後,Worker A 和 Worker B 就可以使用各自收到的 port 直接互相發送和接收訊息,完全繞過主執行緒。 -
優點:
-
效能提升: 減少了訊息傳遞的延遲。
-
減輕主執行緒負擔: 將通訊的負載從主執行緒(負責 UI 渲染和使用者互動)轉移開,避免主執行緒成為瓶頸,提升應用程式的響應速度。
-
-
-
建立專用的通訊管道 (即使是主執行緒與 Worker 之間):
-
問題: 主執行緒和 Worker 之間雖然有預設的
postMessage/onmessage機制,但所有的通訊都混雜在一起。如果你希望針對不同類型的任務或數據流使用不同的處理邏輯,只用預設通道會比較混亂。 -
解決方案: 你可以建立多個
MessageChannel,並將其中一個 port 傳遞給 Worker。例如,一個 Channel 用於發送控制指令,另一個用於傳輸大量數據。 -
優點:
-
更好的程式碼組織: 可以為不同的 port 設定不同的
onmessage處理器,讓邏輯更清晰。 -
區分優先級或類型: 可以更容易地區分和處理不同性質的通訊。
-
-
-
提供一次性的、所有權轉移的通訊端點:
-
MessageChannel的 port 是**可轉移物件 (Transferable Objects)**。當你透過postMessage的第二個參數將 port 轉移給另一個執行緒時,原始執行緒就失去了對該 port 的控制權。 -
優點:
-
明確所有權: 清楚地表明哪個執行緒負責該通訊端點。
-
效能 (間接): 雖然 port 本身不大,但轉移機制是 Web 平台設計中用於高效傳遞資源(尤其是大型資源如
ArrayBuffer)的方式,MessageChannel遵循了這種高效模式。
-
-
-
實現更複雜的通訊模式:
-
有了
MessageChannel,你可以更容易地構建更複雜的架構,例如:-
管道 (Pipeline): 數據在多個 Worker 之間依次處理。
-
扇出/扇入 (Fan-out/Fan-in): 一個 Worker 將任務分發給多個 Worker,然後收集結果。
-
-
總結來說,MessageChannel 提供了一種標準、高效且靈活的方式,用於在不同的 JavaScript 執行上下文之間建立直接、專用的通訊連結,特別是在需要 Worker 之間直接通訊或需要將通訊從主執行緒解耦以提高效能和響應性時,它就顯得非常有用。 它克服了預設 postMessage 機制的某些限制,使得更複雜、更高效的併發程式設計成為可能。