Start · Sprachen · JavaScript · Referenz · MessagePort

MessagePort

Schnittstelle

Einer der beiden Endpunkte eines <code>MessageChannel</code>: Was an einem Port mit <code>postMessage()</code> gesendet wird, kommt am anderen Port als <code>message</code>-Ereignis an.

seit JavaScript Baseline: alle modernen Browser, Node.js 15+ (glob Kategorie: webapi

Signatur

interface MessagePort extends EventTarget

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 man addEventListener("message", …) verwendet. Das Setzen von onmessage ruft start() automatisch auf.
  • close(): trennt den Port. Danach werden keine Nachrichten mehr gesendet oder empfangen.
  • Ereignisse: message, messageerror (Nachricht ließ sich nicht deserialisieren) und close (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

Typ
void
Beschreibung
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');
port2 empfängt: Hallo über den Kanal port1 empfängt: { ok: true }

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' });
  }
};
Summe: 10

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');
});
Vom iframe: Widget bereit

// Wichtig · Fallstricke

  • start() nicht vergessen: Mit addEventListener('message', …) bleiben Nachrichten in der Warteschlange, bis port.start() aufgerufen wird. Bei onmessage passiert 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 einen DataCloneError.
  • 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 alternativ port.unref()).
  • Sicherheit: Wenn der Port per window.postMessage übergeben wird, immer eine konkrete Ziel-Origin angeben und im Empfänger event.origin prüfen. Wer den Port bekommt, kann ohne weitere Prüfung Nachrichten senden. Eingehende Daten wie jede Benutzereingabe behandeln, also z. B. nicht ungeprüft in innerHTML schreiben (XSS).
  • Kompatibilität: postMessage, start, close, message und messageerror werden von allen modernen Browsern unterstützt. Das close-Ereignis gibt es bisher nur in Chromium-Browsern. In Node.js ist MessagePort ab Version 15 global, vorher über require('worker_threads'). Dort erbt es teilweise von EventEmitter (port.on('message', …) liefert direkt die Daten, kein Event-Objekt).