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

ServiceWorker.postMessage()

Methode

Sendet eine Nachricht von der Seite (oder einem anderen Kontext) an einen Service Worker, der sie im <code>message</code>-Event empfängt.

seit JavaScript Baseline: alle modernen Browser (seit 2018) Kategorie: webapi

Signatur

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

Beschreibung

postMessage() auf einem ServiceWorker-Objekt schickt beliebige Daten an den zugehörigen Service Worker. Die Daten werden mit dem Structured-Clone-Algorithmus kopiert. Dadurch lassen sich Objekte, Arrays, Map, Set, Date, Blob, ArrayBuffer usw. übertragen, Funktionen und DOM-Knoten dagegen nicht. Im Worker kommt die Nachricht als ExtendableMessageEvent im message-Event von self an.

Typische Anwendungsfälle sind: den Worker zu einem sofortigen Update auffordern (skipWaiting), bestimmte URLs gezielt in den Cache laden, Caches leeren oder Konfigurationswerte wie Tokens und Sprache übergeben. Ein ServiceWorker-Objekt bekommt man meist über navigator.serviceWorker.controller (der Worker, der die aktuelle Seite steuert) oder über registration.active, .waiting und .installing.

Die Methode antwortet nicht selbst, denn sie gibt undefined zurück. Brauchst du eine Antwort, hast du zwei Möglichkeiten: Du überträgst einen MessagePort aus einem MessageChannel, oder der Worker antwortet über event.source.postMessage(). Auf der Seite empfängst du die Antwort dann mit navigator.serviceWorker.addEventListener('message', …).

Parameter

Name Typ Default Beschreibung
$message Pflicht any Die zu sendenden Daten. Sie müssen mit dem Structured-Clone-Algorithmus kopierbar sein, sonst wird ein DataCloneError ausgelöst.
$transfer Transferable[] [] Objekte, deren Besitz an den Worker übergeben wird, statt sie zu kopieren, z. B. MessagePort oder ArrayBuffer. Nach der Übergabe sind sie im Absender nicht mehr nutzbar.
$options { transfer?: Transferable[] } {} Andere Schreibweise für den zweiten Parameter: ein Objekt mit der Eigenschaft transfer.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Antworten kommen nur über Nachrichten zurück, also über MessagePort oder das message-Event von navigator.serviceWorker.

Beispiele

Einfache Nachricht an den steuernden Service Worker

// Seite (main.js)
if (navigator.serviceWorker.controller) {
  navigator.serviceWorker.controller.postMessage({
    type: 'CACHE_URLS',
    urls: ['/offline.html', '/styles/app.css']
  });
}

// sw.js
self.addEventListener('message', (event) => {
  if (event.data?.type === 'CACHE_URLS') {
    event.waitUntil(
      caches.open('v1').then((cache) => cache.addAll(event.data.urls))
    );
  }
});

Wartenden Worker sofort aktivieren (Update-Banner)

// Seite
const registration = await navigator.serviceWorker.register('/sw.js');

const activateUpdate = () => {
  registration.waiting?.postMessage({ type: 'SKIP_WAITING' });
};

// Seite neu laden, sobald der neue Worker die Kontrolle übernimmt
navigator.serviceWorker.addEventListener('controllerchange', () => {
  window.location.reload();
});

// sw.js
self.addEventListener('message', (event) => {
  if (event.data?.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

Anfrage mit Antwort über MessageChannel

// Seite: Hilfsfunktion, die ein Promise zurückgibt
const askServiceWorker = (message) => new Promise((resolve, reject) => {
  const worker = navigator.serviceWorker.controller;
  if (!worker) return reject(new Error('Kein aktiver Service Worker'));

  const { port1, port2 } = new MessageChannel();
  port1.onmessage = (event) => resolve(event.data);
  worker.postMessage(message, [port2]);
});

const info = await askServiceWorker({ type: 'GET_VERSION' });
console.log('SW-Version:', info.version);

// sw.js
const VERSION = '1.4.2';
self.addEventListener('message', (event) => {
  if (event.data?.type === 'GET_VERSION') {
    event.ports[0].postMessage({ version: VERSION });
  }
});
SW-Version: 1.4.2

Antwort über event.source statt MessageChannel

// sw.js
self.addEventListener('message', (event) => {
  if (event.data?.type === 'PING') {
    event.source.postMessage({ type: 'PONG', time: Date.now() });
  }
});

// Seite
navigator.serviceWorker.addEventListener('message', (event) => {
  if (event.data.type === 'PONG') console.log('Antwort erhalten', event.data.time);
});
navigator.serviceWorker.controller?.postMessage({ type: 'PING' });
Antwort erhalten 1718000000000

// Wichtig · Fallstricke

  • controller kann null sein: Beim ersten Seitenaufruf, bei einem Hard-Reload (Umschalt+F5) und bevor clients.claim() gelaufen ist, wird die Seite von keinem Worker gesteuert. Prüfe das vorher oder warte auf navigator.serviceWorker.ready und nutze dann registration.active.
  • Worker kann schlafen: Der Browser beendet untätige Service Worker. Eine Nachricht weckt ihn zwar auf, aber globale Variablen im Worker gehen dabei verloren. Speichere Zustände deshalb in IndexedDB oder in der Cache API.
  • Längere Arbeiten mit event.waitUntil() absichern, sonst beendet der Browser den Worker womöglich, bevor die Arbeit fertig ist.
  • Structured Clone: Funktionen, DOM-Knoten und Klasseninstanzen samt Prototyp lassen sich nicht übertragen und lösen einen DataCloneError aus bzw. verlieren ihren Prototyp. Übertragene (transferable) ArrayBuffer sind danach im Absender leer.
  • Sicherheit: Service Worker gibt es nur in sicheren Kontexten (HTTPS oder localhost) und nur für denselben Ursprung. Prüfe trotzdem im Worker den Nachrichtentyp und die Daten und vertraue event.data nicht blind. Das gilt besonders, wenn davon Cache-Löschungen oder Netzwerkanfragen abhängen. Bei Bedarf kannst du event.origin bzw. event.source prüfen.
  • Kompatibilität: Die Methode wird in allen modernen Browsern unterstützt (Chrome, Edge, Firefox, Safari 11.1+). Die Schreibweise mit Options-Objekt ({ transfer }) ist neuer. Für ältere Browser verwendest du besser das Array als zweiten Parameter.