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

DedicatedWorkerGlobalScope.postMessage()

Methode

Sendet aus einem Dedicated Worker heraus eine Nachricht an den Thread (meist den Hauptthread), der den Worker erzeugt hat.

seit JavaScript Baseline: alle Browser (Transfer-Options-Objekt ab Kategorie: webapi

Signatur

DedicatedWorkerGlobalScope.postMessage(message, transfer | options)

Beschreibung

postMessage() wird innerhalb eines Dedicated Workers aufgerufen, meist einfach als globales postMessage(...) oder self.postMessage(...). Die Nachricht landet beim zugehörigen Worker-Objekt im erzeugenden Thread. Dort löst sie ein message-Event aus, dessen Eigenschaft event.data die Daten enthält.

Die Daten werden mit dem Structured-Clone-Algorithmus kopiert. Erlaubt sind damit Objekte, Arrays, Map, Set, Date, RegExp, Blob, ArrayBuffer, typisierte Arrays und vieles mehr. Funktionen, DOM-Knoten und Klasseninstanzen samt Prototyp lassen sich nicht übertragen. Der Empfänger bekommt immer eine unabhängige Kopie, nie eine gemeinsame Referenz.

Bei großen Binärdaten kopiert man besser nicht, sondern gibt übertragbare Objekte (ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas, ReadableStream …) über die Transfer-Liste weiter. Dann wechselt der Besitz ohne Kopie. Im Worker ist das Objekt danach unbrauchbar, ein ArrayBuffer hat dann zum Beispiel byteLength === 0.

Typische Einsatzfälle: Ergebnisse rechenintensiver Arbeiten zurückmelden (Parsing, Bildverarbeitung, Kryptografie), Fortschritt melden und Fehler oder Status an die Oberfläche weitergeben, ohne den Hauptthread zu blockieren.

Parameter

Name Typ Default Beschreibung
$message Pflicht any Die zu sendenden Daten. Jeder per Structured Clone kopierbare Wert, z. B. string, number, Objekt, Array, ArrayBuffer oder Blob. Kommt beim Empfänger als event.data an.
$transfer Transferable[] [] Optionales Array übertragbarer Objekte, deren Besitz an den Empfänger übergeht, statt dass sie kopiert werden. Sie müssen auch in message vorkommen, sonst erhält der Empfänger keinen Zugriff darauf.
$options { transfer?: Transferable[] } {} Alternative Form zum Array: ein Objekt mit der Eigenschaft transfer, z. B. postMessage(data, { transfer: [buf] }).

Beispiele

Einfaches Ergebnis an den Hauptthread melden

// worker.js
self.addEventListener('message', (event) => {
  const { numbers } = event.data;
  const sum = numbers.reduce((a, b) => a + b, 0);
  // Ergebnis zurück an den Hauptthread
  postMessage({ type: 'result', sum });
});

// main.js
const worker = new Worker('worker.js');
worker.addEventListener('message', (event) => {
  console.log('Summe:', event.data.sum);
});
worker.postMessage({ numbers: [1, 2, 3, 4, 5] });
Summe: 15

Fortschritt während einer langen Berechnung melden

// worker.js
self.onmessage = ({ data: { limit } }) => {
  const primes = [];
  for (let n = 2; n <= limit; n++) {
    let isPrime = true;
    for (let d = 2; d * d <= n; d++) {
      if (n % d === 0) { isPrime = false; break; }
    }
    if (isPrime) primes.push(n);
    if (n % 25000 === 0) {
      postMessage({ type: 'progress', percent: Math.round((n / limit) * 100) });
    }
  }
  postMessage({ type: 'done', count: primes.length });
};

// main.js
const worker = new Worker('worker.js');
worker.onmessage = ({ data }) => {
  if (data.type === 'progress') console.log(`${data.percent} %`);
  if (data.type === 'done') console.log(`Primzahlen gefunden: ${data.count}`);
};
worker.postMessage({ limit: 100000 });
25 % 50 % 75 % 100 % Primzahlen gefunden: 9592

ArrayBuffer ohne Kopie übertragen (Transferable)

// worker.js
self.onmessage = () => {
  const buffer = new ArrayBuffer(16 * 1024 * 1024); // 16 MB
  new Uint8Array(buffer).fill(42);

  // Besitz übergeben statt kopieren
  postMessage({ buffer }, { transfer: [buffer] });
  // Alternativ: postMessage({ buffer }, [buffer]);

  console.log('Im Worker danach:', buffer.byteLength);
};

// main.js
const worker = new Worker('worker.js');
worker.onmessage = ({ data }) => {
  const view = new Uint8Array(data.buffer);
  console.log('Empfangen:', view.byteLength, 'Bytes, erstes Byte =', view[0]);
};
worker.postMessage('start');
Im Worker danach: 0 Empfangen: 16777216 Bytes, erstes Byte = 42

Fehler strukturiert zurückmelden

// worker.js
self.onmessage = ({ data }) => {
  try {
    const parsed = JSON.parse(data);
    postMessage({ ok: true, parsed });
  } catch (err) {
    // Error-Objekte sind klonbar, eigene Eigenschaften aber nicht garantiert
    postMessage({ ok: false, message: err.message });
  }
};

// main.js
const worker = new Worker('worker.js');
worker.onmessage = ({ data }) => {
  console.log(data.ok ? data.parsed : `Fehler: ${data.message}`);
};
worker.postMessage('{ kaputt');
Fehler: Expected property name or '}' in JSON at position 2 (Text variiert je nach Engine)

// Wichtig · Fallstricke

  • DataCloneError: Nicht klonbare Werte wie Funktionen, DOM-Knoten, Symbole oder Objekte mit Funktionen als Eigenschaften lösen synchron eine DOMException vom Typ DataCloneError aus. Klasseninstanzen kommen als einfache Objekte ohne Prototyp und Methoden an.
  • Kein targetOrigin: Anders als window.postMessage() gibt es hier keinen Ursprungsparameter, denn der Empfänger ist immer eindeutig der erzeugende Thread.
  • Transfer-Fallen: Nach dem Übertragen ist das Objekt im Worker abgekoppelt (detached). Wer es weiter benutzt, bekommt leere Puffer oder einen TypeError. Steht dasselbe Objekt doppelt in der Transfer-Liste oder ist es schon abgekoppelt, wird ein DataCloneError ausgelöst.
  • Leistung: Das Klonen großer Objektbäume kostet auf beiden Seiten spürbar Zeit. Für Binärdaten deshalb Transferables oder SharedArrayBuffer verwenden. Letzterer setzt Cross-Origin-Isolation (COOP/COEP-Header) voraus.
  • Sicherheit: Empfangene Daten im Hauptthread nicht ungeprüft in innerHTML einfügen, vor allem wenn der Worker Fremddaten verarbeitet. Sonst droht XSS.
  • Kompatibilität: In allen modernen Browsern verfügbar. Die Form mit Options-Objekt ({ transfer }) fehlt in sehr alten Browsern, dort das Array verwenden. Bei Shared Workers gibt es diese Methode nicht, dort läuft die Kommunikation über MessagePort.postMessage().