Signatur
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
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() });
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');
// });
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
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);
// Wichtig · Fallstricke
- Port starten: Wer
addEventListener('message', …)stattonmessagenutzt, mussport.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
ArrayBufferhat dannbyteLength === 0, ein übertragenerMessagePortkann 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(perwindow.postMessage()) und prüfen Sie beim ersten Handshakeevent.origin. Empfangene Daten sind wie jede Fremdeingabe zu behandeln: nie ungeprüft ininnerHTMLoderevaleinsetzen (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 esMessagePortausworker_threadsmit gleicher Semantik (dort perEventEmitterundport.on('message')).