Signatur
Beschreibung
MessagePort gehört zur Channel Messaging API. Ein MessagePort wird nie direkt erzeugt, sondern entsteht immer paarweise über new MessageChannel() (Eigenschaften port1 und port2). Die beiden Ports sind miteinander verbunden: Nachrichten, die an einem Port mit postMessage() abgeschickt werden, lösen am Gegenstück ein message-Ereignis aus.
Richtig nützlich wird das, weil sich ein Port als übertragbares Objekt (Transferable) an einen anderen Kontext weitergeben lässt, etwa an einen Web Worker, einen Service Worker, ein iframe oder ein anderes Fenster. So entsteht ein eigener, privater Zweiwege-Kanal, der vom allgemeinen postMessage-Verkehr getrennt ist. Typische Anwendungen sind Request/Response-Protokolle mit Workern, direkte Kommunikation zwischen zwei Workern und abgesicherte Kanäle zu eingebetteten Frames.
Wichtige Mitglieder:
postMessage(message, transfer)bzw.postMessage(message, { transfer }): sendet eine Nachricht (structured clone) an den verbundenen Port.start(): startet die Zustellung der Nachrichten aus der Warteschlange. Das ist nur nötig, wenn manaddEventListener("message", …)verwendet. Das Setzen vononmessageruftstart()automatisch auf.close(): trennt den Port. Danach werden keine Nachrichten mehr gesendet oder empfangen.- Ereignisse:
message,messageerror(Nachricht ließ sich nicht deserialisieren) undclose(neu, nur Chromium: Gegenstelle wurde geschlossen).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $message Pflicht | any | Parameter von postMessage(): beliebiger Wert, der mit dem Structured-Clone-Algorithmus kopiert wird. Funktionen, DOM-Knoten und ähnliche Objekte sind nicht erlaubt. |
|
| $transfer | Transferable[] | { transfer: Transferable[] } | [] | Parameter von postMessage(): Objekte wie ArrayBuffer, MessagePort oder OffscreenCanvas, deren Besitz übertragen wird, statt sie zu kopieren. Im sendenden Kontext sind sie danach unbrauchbar. |
Rückgabewert
postMessage(), start() und close() geben undefined zurück. Empfangene Daten stehen im data-Feld des MessageEvent.Beispiele
Grundlagen: zwei verbundene Ports im selben Kontext
const { port1, port2 } = new MessageChannel();
// onmessage ruft start() automatisch auf
port2.onmessage = (event) => {
console.log('port2 empfängt:', event.data);
port2.postMessage({ ok: true });
};
port1.addEventListener('message', (event) => {
console.log('port1 empfängt:', event.data);
port1.close();
});
port1.start(); // bei addEventListener nötig!
port1.postMessage('Hallo über den Kanal');
Request/Response mit einem Web Worker (Port pro Anfrage)
// main.js
const worker = new Worker('worker.js');
const ask = (payload) => new Promise((resolve, reject) => {
const { port1, port2 } = new MessageChannel();
port1.onmessage = ({ data }) => {
port1.close();
data.error ? reject(new Error(data.error)) : resolve(data.result);
};
// port2 wird an den Worker übertragen, nicht kopiert
worker.postMessage(payload, [port2]);
});
const summe = await ask({ op: 'sum', values: [1, 2, 3, 4] });
console.log('Summe:', summe);
// worker.js
self.onmessage = ({ data, ports: [replyPort] }) => {
if (data.op === 'sum') {
replyPort.postMessage({ result: data.values.reduce((a, b) => a + b, 0) });
} else {
replyPort.postMessage({ error: 'Unbekannte Operation' });
}
};
Privater Kanal zu einem iframe
// Elternseite
const iframe = document.querySelector('iframe');
const { port1, port2 } = new MessageChannel();
iframe.addEventListener('load', () => {
// Ziel-Origin explizit angeben, niemals '*'
iframe.contentWindow.postMessage('init', 'https://widget.example.com', [port2]);
});
port1.onmessage = ({ data }) => console.log('Vom iframe:', data);
// Im iframe (https://widget.example.com)
window.addEventListener('message', (event) => {
if (event.origin !== 'https://app.example.com' || event.data !== 'init') return;
const port = event.ports[0];
port.postMessage('Widget bereit');
});
// Wichtig · Fallstricke
start()nicht vergessen: MitaddEventListener('message', …)bleiben Nachrichten in der Warteschlange, bisport.start()aufgerufen wird. Beionmessagepassiert das automatisch.- Übertragen statt kopieren: Ein Port muss im
transfer-Array stehen. Danach ist er im sendenden Kontext unbrauchbar. Wird ein Port ohne Transfer mitgeschickt, gibt es einenDataCloneError. - Aufräumen: Offene Ports mit aktiven Listenern können den Garbage Collector aufhalten und einen Node.js-Prozess am Beenden hindern. Nicht mehr benötigte Ports mit
close()schließen (in Node.js alternativport.unref()). - Sicherheit: Wenn der Port per
window.postMessageübergeben wird, immer eine konkrete Ziel-Origin angeben und im Empfängerevent.originprüfen. Wer den Port bekommt, kann ohne weitere Prüfung Nachrichten senden. Eingehende Daten wie jede Benutzereingabe behandeln, also z. B. nicht ungeprüft ininnerHTMLschreiben (XSS). - Kompatibilität:
postMessage,start,close,messageundmessageerrorwerden von allen modernen Browsern unterstützt. Dasclose-Ereignis gibt es bisher nur in Chromium-Browsern. In Node.js istMessagePortab Version 15 global, vorher überrequire('worker_threads'). Dort erbt es teilweise vonEventEmitter(port.on('message', …)liefert direkt die Daten, kein Event-Objekt).