Start · Sprachen · JavaScript · Referenz · ServiceWorkerGlobalScope: message

ServiceWorkerGlobalScope: message

Ereignis

Wird im Service Worker ausgelöst, wenn eine Nachricht eingeht, z. B. per <code>navigator.serviceWorker.controller.postMessage()</code> aus einer Seite.

seit JavaScript Baseline: alle modernen Browser (Chrome 40+, Firef Kategorie: webapi

Signatur

self.addEventListener("message", (event) => { … })

Beschreibung

Das message-Ereignis des ServiceWorkerGlobalScope tritt auf, wenn der Service Worker eine Nachricht empfängt. Meist kommt sie von einer kontrollierten Seite, die navigator.serviceWorker.controller.postMessage() oder registration.active.postMessage() aufruft. Der Handler bekommt ein ExtendableMessageEvent. Dessen Eigenschaft data enthält die Nutzdaten, source verweist auf den sendenden Client (bzw. ServiceWorker oder MessagePort).

Registrieren kannst du den Handler auf zwei Wegen: mit self.addEventListener("message", handler) oder über die Eigenschaft self.onmessage = handler. Typische Anwendungsfälle sind:

  • skipWaiting() auf Knopfdruck auslösen (Muster „Update verfügbar“)
  • den Cache gezielt befüllen oder leeren
  • den Status des Workers abfragen
  • Daten an andere Tabs weiterverteilen

Weil ExtendableMessageEvent von ExtendableEvent erbt, kannst du mit event.waitUntil(promise) dafür sorgen, dass der Browser den Worker erst beendet, wenn die asynchrone Arbeit abgeschlossen ist. Antworten schickst du entweder direkt über event.source.postMessage() oder über einen mitgeschickten MessagePort in event.ports.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignistyp, hier "message".
$listener Pflicht (event: ExtendableMessageEvent) => void Handler-Funktion. Wichtige Eigenschaften des Ereignisobjekts: data (Nutzdaten), source (Client | ServiceWorker | MessagePort | null), origin, ports (übertragene MessagePort-Objekte), lastEventId.
$options boolean | AddEventListenerOptions false Optionale Listener-Optionen wie once oder signal.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Den Rückgabewert des Handlers ignoriert der Browser. Für asynchrone Arbeit verwendest du event.waitUntil().

Beispiele

SKIP_WAITING-Muster: Update auf Nutzerwunsch aktivieren

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

// main.js (Seite)
const reg = await navigator.serviceWorker.getRegistration();
if (reg?.waiting) {
  reg.waiting.postMessage({ type: "SKIP_WAITING" });
}

Antwort an den sendenden Tab mit waitUntil

// sw.js
self.addEventListener("message", (event) => {
  if (event.data?.type !== "CACHE_URLS") return;

  event.waitUntil((async () => {
    const cache = await caches.open("runtime-v1");
    await cache.addAll(event.data.urls);
    // event.source ist der Client, der die Nachricht geschickt hat
    event.source?.postMessage({ type: "CACHED", count: event.data.urls.length });
  })());
});

// main.js
navigator.serviceWorker.addEventListener("message", (e) => console.log(e.data));
navigator.serviceWorker.controller?.postMessage({
  type: "CACHE_URLS",
  urls: ["/", "/styles.css", "/app.js"]
});
{ type: "CACHED", count: 3 }

Anfrage/Antwort über MessageChannel (onmessage-Property)

// sw.js
const VERSION = "1.4.2";
self.onmessage = (event) => {
  if (event.data === "GET_VERSION") {
    event.ports[0]?.postMessage(VERSION);
  }
};

// main.js
const askVersion = () => new Promise((resolve) => {
  const { port1, port2 } = new MessageChannel();
  port1.onmessage = (e) => resolve(e.data);
  navigator.serviceWorker.controller.postMessage("GET_VERSION", [port2]);
});

console.log("SW-Version:", await askVersion());
SW-Version: 1.4.2

Nachricht an alle offenen Tabs verteilen

// sw.js
self.addEventListener("message", (event) => {
  if (event.data?.type !== "BROADCAST") return;
  event.waitUntil((async () => {
    const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
    for (const client of clients) {
      if (client.id !== event.source?.id) {
        client.postMessage({ type: "SYNC", payload: event.data.payload });
      }
    }
  })());
});

// Wichtig · Fallstricke

  • Absender prüfen: Nachrichten können nur von Clients desselben Ursprungs kommen. Validiere trotzdem event.data (Typ, Struktur), bevor du davon abhängige Aktionen wie Cache-Löschung ausführst. Werte in data gelten als nicht vertrauenswürdige Eingabe.
  • Kein Controller: Bei einem harten Neuladen (Umschalt+F5) oder beim ersten Besuch ist navigator.serviceWorker.controller gleich null. Nutze dann registration.active oder warte auf navigator.serviceWorker.ready.
  • Lebensdauer: Nach dem Ereignis kann der Browser den Worker jederzeit beenden. Asynchrone Arbeit gehört deshalb in event.waitUntil(). Globale Variablen bleiben zwischen den Ereignissen nicht zuverlässig erhalten.
  • Serialisierung: data wird per Structured Clone übertragen. Funktionen, DOM-Knoten und Klasseninstanzen (deren Prototyp verloren geht) lassen sich nicht senden. Große Puffer überträgst du effizient als Transferable.
  • Kann eine Nachricht nicht deserialisiert werden, feuert stattdessen messageerror.
  • Die Seite empfängt Antworten über navigator.serviceWorker (message-Ereignis am ServiceWorkerContainer), nicht über window. Service Worker laufen nur in sicheren Kontexten (HTTPS oder localhost).