Start · Sprachen · JavaScript · Referenz · MessagePort.postMessage()

MessagePort.postMessage()

Methode

Sendet eine Nachricht über einen <code>MessagePort</code> an den verbundenen Gegenport und überträgt optional die Eigentümerschaft übertragbarer Objekte.

seit JavaScript Baseline: alle modernen Browser; options-Variante Kategorie: webapi

Signatur

MessagePort.prototype.postMessage(message, transfer) / MessagePort.prototype.postMessage(message, options)

Beschreibung

postMessage() schickt eine Nachricht an den verbundenen Gegenport eines MessageChannel. Die Ports eines Kanals sind fest miteinander gekoppelt. Deshalb gibt es hier, anders als bei window.postMessage(), keinen targetOrigin-Parameter: Die Nachricht landet immer beim Gegenport, egal in welchem Fenster, iframe oder Worker er gerade liegt.

Die Nachricht wird mit dem Structured-Clone-Algorithmus kopiert. Erlaubt sind also einfache Werte, Objekte, Arrays, Map, Set, Date, Blob, ArrayBuffer und Ähnliches. Funktionen, DOM-Knoten und Klasseninstanzen samt Prototyp lassen sich nicht übertragen. Mit dem zweiten Argument übergeben Sie übertragbare Objekte (ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas, Streams …). Diese werden nicht kopiert, sondern ohne Kopierkosten verschoben. Auf der Senderseite sind sie danach nicht mehr benutzbar.

Typische Einsatzfälle:

  • ein eigener, privater Kanal zwischen Hauptseite und iframe
  • direkte Kommunikation zwischen zwei Workern
  • Anfrage/Antwort-Muster, bei dem jede Anfrage einen eigenen Antwort-Port mitbringt
  • Datenübergabe an Service Worker

Die Zustellung läuft asynchron. Beim Empfänger wird ein message-Ereignis ausgelöst. Hat der Empfänger seinen Port mit addEventListener verbunden, muss er vorher port.start() aufrufen. Bis dahin werden Nachrichten gepuffert.

Parameter

Name Typ Default Beschreibung
$message Pflicht any Die zu sendenden Daten. Sie müssen per Structured Clone serialisierbar sein, sonst wird eine DataCloneError-DOMException ausgelöst.
$transfer Array<Transferable> [] Optionales Array übertragbarer Objekte, deren Eigentümerschaft an den Empfänger übergeht. Die Objekte müssen auch in message enthalten (referenziert) sein, damit der Empfänger sie erhält.
$options { transfer?: Array<Transferable> } {} Alternative Form zum zweiten Argument: ein Objekt mit der Eigenschaft transfer.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert (undefined). Ob die Nachricht angekommen ist, erfahren Sie nur über eine Antwort des Empfängers.

Beispiele

Einfacher Kanal innerhalb derselben Seite

const { port1, port2 } = new MessageChannel();

// onmessage startet den Port automatisch
port2.onmessage = (event) => {
  console.log('Empfangen:', event.data);
  port2.postMessage({ antwort: 'pong' });
};

port1.onmessage = (event) => console.log('Antwort:', event.data);

port1.postMessage({ typ: 'ping', zeit: Date.now() });
Empfangen: { typ: 'ping', zeit: 1700000000000 } Antwort: { antwort: 'pong' }

Privaten Kanal zu einem iframe aufbauen

// Hauptseite
const iframe = document.querySelector('#widget');
const channel = new MessageChannel();

channel.port1.onmessage = (event) => {
  console.log('Vom iframe:', event.data);
};

iframe.addEventListener('load', () => {
  // port2 wird an das iframe übertragen – mit strengem targetOrigin
  iframe.contentWindow.postMessage('init', 'https://widget.example.com', [channel.port2]);
  channel.port1.postMessage({ theme: 'dark' });
});

// Im iframe (https://widget.example.com):
// window.addEventListener('message', (e) => {
//   if (e.origin !== 'https://app.example.com') return;
//   const [port] = e.ports;
//   port.addEventListener('message', (m) => console.log('Config:', m.data));
//   port.start(); // nötig bei addEventListener
//   port.postMessage('bereit');
// });
Vom iframe: bereit

ArrayBuffer ohne Kopie an einen Worker übertragen

const worker = new Worker('verarbeitung.js');
const { port1, port2 } = new MessageChannel();

// Port an den Worker übergeben
worker.postMessage({ port: port2 }, [port2]);

const puffer = new Float32Array(1_000_000).buffer;
console.log('Vorher:', puffer.byteLength);

// Options-Form: Eigentümerschaft des Puffers geht über
port1.postMessage({ daten: puffer }, { transfer: [puffer] });

console.log('Nachher:', puffer.byteLength); // abgekoppelt
Vorher: 4000000 Nachher: 0

Anfrage/Antwort mit Promise und eigenem Antwort-Port

const anfrage = (zielPort, payload) =>
  new Promise((resolve) => {
    const { port1, port2 } = new MessageChannel();
    port1.onmessage = (event) => {
      resolve(event.data);
      port1.close();
    };
    zielPort.postMessage(payload, [port2]);
  });

// Gegenseite (Demo im selben Kontext)
const server = new MessageChannel();
server.port2.onmessage = ({ data, ports: [antwortPort] }) => {
  antwortPort.postMessage(data.a + data.b);
};

const ergebnis = await anfrage(server.port1, { a: 2, b: 3 });
console.log('Summe:', ergebnis);
Summe: 5

// Wichtig · Fallstricke

  • Port starten: Wer addEventListener('message', …) statt onmessage nutzt, muss port.start() aufrufen. Sonst kommen keine Nachrichten an.
  • DataCloneError: Funktionen, DOM-Knoten, Symbole und Objekte mit Getter-Logik lassen sich nicht klonen. Ein bereits übertragenes (abgekoppeltes) Objekt ein zweites Mal zu übertragen, löst ebenfalls einen Fehler aus.
  • Übertragene Objekte sind danach beim Sender unbrauchbar: Ein ArrayBuffer hat dann byteLength === 0, ein übertragener MessagePort kann nicht mehr senden.
  • Sicherheit: Der Kanal selbst ist privat. Wer aber einen Port besitzt, kann uneingeschränkt senden. Übergeben Sie Ports deshalb nur mit strengem targetOrigin (per window.postMessage()) und prüfen Sie beim ersten Handshake event.origin. Empfangene Daten sind wie jede Fremdeingabe zu behandeln: nie ungeprüft in innerHTML oder eval einsetzen (XSS).
  • Nach close(): Nach port.close() werden Nachrichten stillschweigend verworfen, es gibt keine Fehlermeldung.
  • Speicher: Ungeschlossene Ports mit aktivem Listener können den Garbage Collector blockieren. Schließen Sie Antwort-Ports nach Gebrauch.
  • Kompatibilität: Die Form mit options-Objekt ist in älteren Browsern nicht verfügbar. Die Array-Form funktioniert überall. In Node.js gibt es MessagePort aus worker_threads mit gleicher Semantik (dort per EventEmitter und port.on('message')).