Start · Sprachen · JavaScript · Referenz · SharedWorker.port

SharedWorker.port

Eigenschaft

Schreibgeschützte Eigenschaft, die das <code>MessagePort</code>-Objekt liefert, über das die Seite mit dem Shared Worker kommuniziert.

seit JavaScript Baseline 2024 (Chrome 4+, Firefox 29+, Safari 16+; Kategorie: webapi

Signatur

SharedWorker.port

Beschreibung

Die schreibgeschützte Eigenschaft port eines SharedWorker-Objekts liefert einen MessagePort. Anders als bei einem dedizierten Worker hat der Shared Worker selbst keine postMessage()-Methode. Die gesamte Kommunikation läuft über diesen Port.

Ein Shared Worker kann gleichzeitig von mehreren Browsing-Kontexten desselben Ursprungs genutzt werden, also von Tabs, Fenstern oder iframes. Jede Seite, die new SharedWorker(url) aufruft, bekommt einen eigenen Port. Im Worker kommt dieser als event.ports[0] im connect-Ereignis an. So kann der Worker jeden Client einzeln ansprechen oder Nachrichten an alle verbinden Ports verteilen.

Wichtig ist das Starten des Ports: Wer einen Listener über port.onmessage = … setzt, startet den Port automatisch. Wer port.addEventListener("message", …) verwendet, muss port.start() selbst aufrufen. Sonst werden Nachrichten zwar gepuffert, aber nie zugestellt.

Typische Einsatzfälle sind eine gemeinsame WebSocket-Verbindung für alle Tabs, zentrales Zwischenspeichern, das Abgleichen von Zuständen zwischen Tabs (z. B. Login/Logout) oder das Bündeln aufwendiger Berechnungen.

Rückgabewert

Typ
MessagePort
Beschreibung
Der Port zum Senden (postMessage()) und Empfangen (message-Ereignis) von Nachrichten zwischen der Seite und dem Shared Worker. Es ist bei jedem Zugriff dasselbe Objekt.

Beispiele

Grundlegende Kommunikation mit onmessage

// main.js
const worker = new SharedWorker('counter-worker.js');

// onmessage startet den Port automatisch
worker.port.onmessage = (event) => {
  console.log('Antwort vom Worker:', event.data);
};

worker.port.postMessage({ type: 'increment' });

// counter-worker.js
let count = 0;
onconnect = (event) => {
  const port = event.ports[0];
  port.onmessage = (e) => {
    if (e.data.type === 'increment') {
      count += 1;
      port.postMessage({ count });
    }
  };
};
Antwort vom Worker: { count: 1 }

addEventListener mit explizitem start()

const worker = new SharedWorker('worker.js', { name: 'app-sync' });
const { port } = worker;

const handleMessage = (event) => {
  console.log('Empfangen:', event.data);
};

port.addEventListener('message', handleMessage);
port.addEventListener('messageerror', (e) => {
  console.error('Nachricht nicht deserialisierbar', e);
});

// Ohne start() kommen bei addEventListener keine Nachrichten an!
port.start();

port.postMessage('ping');
Empfangen: pong

Nachricht an alle Tabs verteilen (Broadcast)

// broadcast-worker.js
const ports = new Set();

onconnect = (event) => {
  const port = event.ports[0];
  ports.add(port);

  port.onmessage = ({ data }) => {
    if (data.type === 'bye') {
      ports.delete(port);
      return;
    }
    // An alle verbundenen Tabs weiterleiten
    for (const p of ports) p.postMessage(data);
  };
};

// main.js (in jedem Tab)
const worker = new SharedWorker('broadcast-worker.js');
worker.port.onmessage = ({ data }) => {
  if (data.type === 'logout') location.href = '/login';
};

window.addEventListener('pagehide', () => {
  worker.port.postMessage({ type: 'bye' });
  worker.port.close();
});

document.querySelector('#logout')?.addEventListener('click', () => {
  worker.port.postMessage({ type: 'logout' });
});

Promise-basierte Anfrage über den Port

const worker = new SharedWorker('api-worker.js');
worker.port.start();

const request = (payload) => new Promise((resolve) => {
  const id = crypto.randomUUID();
  const onMessage = ({ data }) => {
    if (data.id !== id) return;
    worker.port.removeEventListener('message', onMessage);
    resolve(data.result);
  };
  worker.port.addEventListener('message', onMessage);
  worker.port.postMessage({ id, payload });
});

const result = await request({ action: 'getUser', userId: 42 });
console.log(result);
{ id: 42, name: 'Anna' }

// Wichtig · Fallstricke

  • Port starten: Bei addEventListener("message", …) muss port.start() aufgerufen werden. Nur die Zuweisung an onmessage startet den Port automatisch. Das gilt auf beiden Seiten, also auch im Worker.
  • Kein Ereignis beim Trennen: Schließt ein Tab, erfährt der Worker das nicht zuverlässig. Veraltete Ports sollte man deshalb selbst aufräumen, etwa mit einer „bye“-Nachricht bei pagehide oder einem Heartbeat. Sonst wächst die Port-Liste immer weiter.
  • Gleicher Ursprung: Shared Worker werden nur zwischen Kontexten mit gleichem Ursprung, gleicher URL und gleichem name geteilt. Partitionierung bei Drittanbietern und private Fenster können eigene Instanzen erzeugen.
  • Kompatibilität: Chrome für Android und ältere Safari-Versionen vor 16 unterstützen SharedWorker nicht. Vorher mit 'SharedWorker' in window prüfen und bei Bedarf auf BroadcastChannel oder einen dedizierten Worker ausweichen.
  • Daten: Nachrichten werden mit dem Structured-Clone-Algorithmus kopiert. Funktionen und DOM-Knoten lassen sich nicht übertragen und lösen einen DataCloneError aus. Große ArrayBuffer sollte man als übertragbare Objekte (Transferables) übergeben.
  • Sicherheit: Eingehende Nachrichten sollten im Worker geprüft werden, denn jeder Tab desselben Ursprungs kann sich verbinden.