Start · Sprachen · JavaScript · Referenz · MessagePort: message

MessagePort: message

Ereignis

Wird auf einem <code>MessagePort</code> ausgelöst, sobald über den zugehörigen Kanal eine Nachricht eintrifft; die Daten stehen in <code>event.data</code>.

seit JavaScript Baseline: alle modernen Browser (seit 2015), Web W Kategorie: webapi

Signatur

port.addEventListener("message", handler)

Beschreibung

Das Ereignis message wird auf einem MessagePort ausgelöst, wenn der Gegenport (das andere Ende eines MessageChannel) mit postMessage() eine Nachricht sendet. Der Handler bekommt ein MessageEvent. Die Nutzdaten stehen in event.data, übertragene Ports in event.ports.

Einsatzgebiete sind vor allem direkte, private Kommunikationskanäle, etwa zwischen Hauptseite und iframe, zwischen Hauptthread und Worker, bei SharedWorker-Verbindungen oder zwischen zwei Workern. Anders als window.postMessage() ist ein Port ein 1:1-Kanal. Wer den Port besitzt, kann Nachrichten senden und empfangen.

Einen Handler registrierst du auf zwei Arten: mit port.addEventListener("message", handler) oder über die Eigenschaft port.onmessage = handler. Das ist ein wichtiger Unterschied: Das Setzen von onmessage ruft intern port.start() auf. Bei addEventListener musst du port.start() selbst aufrufen. Bis dahin werden eingehende Nachrichten nur in einer Warteschlange gesammelt.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "message".
$handler Pflicht (event: MessageEvent) => void Callback, der ein MessageEvent erhält. Wichtige Eigenschaften: data (Nutzdaten, per structured clone kopiert), ports (übertragene MessagePort-Objekte), origin und lastEventId (bei Ports meist leere Zeichenkette).
$options boolean|AddEventListenerOptions false Optionen wie once, signal (AbortSignal zum Entfernen) oder capture.

Rückgabewert

Typ
void
Beschreibung
addEventListener() gibt nichts zurück. Der Handler bekommt ein MessageEvent.

Beispiele

Einfacher Kanal mit addEventListener und start()

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

port2.addEventListener('message', (event) => {
  console.log('Empfangen:', event.data);
});
// Bei addEventListener Pflicht, sonst bleiben die Nachrichten in der Warteschlange
port2.start();

port1.postMessage({ typ: 'gruss', text: 'Hallo Port!' });
Empfangen: { typ: 'gruss', text: 'Hallo Port!' }

onmessage-Eigenschaft: Kommunikation mit einem iframe

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

// onmessage ruft port.start() automatisch auf
channel.port1.onmessage = (event) => {
  console.log('Antwort vom iframe:', event.data);
};

iframe.addEventListener('load', () => {
  // port2 an das iframe übertragen (Transfer-Liste!)
  iframe.contentWindow.postMessage('init', 'https://widget.example.com', [channel.port2]);
});

// Im iframe:
// window.addEventListener('message', (e) => {
//   if (e.origin !== 'https://meine-seite.example') return;
//   const port = e.ports[0];
//   port.onmessage = (ev) => port.postMessage(`Echo: ${ev.data}`);
//   port.postMessage('bereit');
// });
Antwort vom iframe: bereit

Request/Response-Muster mit Promise und Listener entfernen

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

// "Server"-Seite: verdoppelt Zahlen
port2.onmessage = ({ data }) => {
  port2.postMessage({ id: data.id, ergebnis: data.wert * 2 });
};

const anfrage = (wert) => new Promise((resolve) => {
  const id = crypto.randomUUID();
  const handler = (event) => {
    if (event.data.id !== id) return;
    port1.removeEventListener('message', handler);
    resolve(event.data.ergebnis);
  };
  port1.addEventListener('message', handler);
  port1.start();
  port1.postMessage({ id, wert });
});

const ergebnis = await anfrage(21);
console.log(ergebnis);
42

SharedWorker: message auf dem Verbindungsport

// shared-worker.js
const verbindungen = [];
self.addEventListener('connect', (event) => {
  const port = event.ports[0];
  verbindungen.push(port);
  port.onmessage = (e) => {
    // Nachricht an alle verbundenen Tabs verteilen
    verbindungen.forEach((p) => p.postMessage(e.data));
  };
});

// main.js
const worker = new SharedWorker('shared-worker.js');
worker.port.onmessage = (e) => console.log('Broadcast:', e.data);
worker.port.postMessage('Hallo aus Tab A');
Broadcast: Hallo aus Tab A

// Wichtig · Fallstricke

  • start() nicht vergessen: Bei addEventListener werden Nachrichten erst zugestellt, wenn port.start() aufgerufen wurde. onmessage erledigt das automatisch. Dieser Fehler ist bei Ports sehr häufig.
  • Kopie statt Referenz: event.data wird per structured clone kopiert. Funktionen, DOM-Knoten und Klasseninstanzen samt Prototyp gehen dabei nicht mit. Sind die Daten nicht klonbar, wirft schon postMessage() einen DataCloneError.
  • Deserialisierungsfehler: Kann eine Nachricht beim Empfänger nicht deserialisiert werden, wird statt message das Ereignis messageerror ausgelöst. Dafür solltest du einen eigenen Handler registrieren.
  • Sicherheit: event.origin ist bei MessagePort in der Regel leer. Die Vertrauensprüfung gehört daher an die Stelle, an der der Port übergeben wird, also in den window-message-Handler mit e.origin-Prüfung. Eingehende Daten solltest du nie ungeprüft in innerHTML oder eval verwenden (XSS-Gefahr).
  • Übertragen statt kopieren: Ports werden über die Transfer-Liste von postMessage() übertragen. Danach sind sie im Ursprungskontext nicht mehr nutzbar.
  • Aufräumen: Mit port.close() schließt du den Kanal. Solange ein onmessage-Handler gesetzt ist, kann ein Port Speicherbereinigung verhindern.
  • Kompatibilität: Das Ereignis wird von allen modernen Browsern sowie in Workern unterstützt. Node.js bietet ein ähnliches MessagePort im Modul worker_threads. Dort starten sowohl on('message') als auch addEventListener den Port.