Start · Sprachen · JavaScript · Referenz · ServiceWorker

ServiceWorker

Schnittstelle

Das <code>ServiceWorker</code>-Interface ist eine Referenz auf einen konkreten Service Worker. Du kannst damit dessen Zustand abfragen und ihm Nachrichten schicken.

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

Signatur

interface ServiceWorker extends EventTarget

Beschreibung

Ein ServiceWorker-Objekt steht für einen einzelnen Service Worker, der unter einer URL registriert ist. Die Instanz legst du nie selbst mit new an. Du bekommst sie über navigator.serviceWorker.controller (der Worker, der die aktuelle Seite steuert) oder über die Eigenschaften installing, waiting und active einer ServiceWorkerRegistration.

Das Interface hat nur wenige Mitglieder:

  • scriptURL – die URL des Worker-Skripts (nur lesbar)
  • state – der Lebenszyklus-Zustand: "parsed", "installing", "installed", "activating", "activated" oder "redundant"
  • postMessage(message, transfer) – schickt dem Worker eine Nachricht, die dieser im message-Event erhält
  • statechange-Event bzw. onstatechange – wird ausgelöst, sobald sich state ändert
  • error-Event bzw. onerror – wird bei Fehlern im Worker ausgelöst

Du brauchst das Interface vor allem an zwei Stellen: um den Update-Ablauf zu steuern (etwa einen Hinweis „Neue Version verfügbar“ anzeigen, sobald ein neuer Worker installed ist) und um zwischen Seite und Worker zu kommunizieren (etwa das Signal SKIP_WAITING schicken oder Caches gezielt leeren).

Service Worker laufen nur in einem sicheren Kontext, also über HTTPS oder auf localhost. Ihr Lebenszyklus ist unabhängig von einzelnen Tabs: Der Browser kann einen Worker jederzeit beenden und bei Bedarf neu starten.

Parameter

Name Typ Default Beschreibung
$message Pflicht any Nur bei postMessage(): Die Daten, die an den Worker gehen. Sie werden mit dem Structured-Clone-Algorithmus kopiert, deshalb sind keine Funktionen und keine DOM-Knoten erlaubt.
$transfer Transferable[] | { transfer: Transferable[] } [] Nur bei postMessage(): Objekte, deren Besitz an den Worker übergeht, statt dass sie kopiert werden, z. B. MessagePort oder ArrayBuffer.

Beispiele

Worker registrieren und aktive Instanz auslesen

const init = async () => {
  if (!('serviceWorker' in navigator)) return;

  const registration = await navigator.serviceWorker.register('/sw.js');
  // Welcher Worker gerade vorhanden ist, hängt vom Zustand ab
  const worker = registration.installing ?? registration.waiting ?? registration.active;

  console.log(worker.scriptURL, worker.state);
};

init();
https://example.com/sw.js installing

Lebenszyklus mit statechange verfolgen und Update-Hinweis zeigen

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

registration.addEventListener('updatefound', () => {
  const newWorker = registration.installing;

  const onStateChange = () => {
    console.log('Neuer Zustand:', newWorker.state);
    // Ein alter Controller ist vorhanden, also handelt es sich um ein Update
    if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
      console.log('Neue Version bereit – bitte neu laden.');
      newWorker.removeEventListener('statechange', onStateChange);
    }
  };

  newWorker.addEventListener('statechange', onStateChange);
});
Neuer Zustand: installed Neue Version bereit – bitte neu laden.

Wartenden Worker per postMessage sofort aktivieren

// Seite:
const registration = await navigator.serviceWorker.getRegistration();
registration?.waiting?.postMessage({ type: 'SKIP_WAITING' });

navigator.serviceWorker.addEventListener('controllerchange', () => {
  window.location.reload();
});

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

Antwort vom Worker über einen MessageChannel

const askWorker = (payload) => new Promise((resolve) => {
  const { port1, port2 } = new MessageChannel();
  port1.onmessage = (event) => resolve(event.data);
  // port2 geht per transfer an den Worker über
  navigator.serviceWorker.controller?.postMessage(payload, [port2]);
});

// sw.js: event.ports[0].postMessage({ version: '1.4.0' });
const answer = await askWorker({ type: 'GET_VERSION' });
console.log(answer.version);
1.4.0

// Wichtig · Fallstricke

  • Controller ist oft null: Beim ersten Laden steuert noch kein Worker die Seite, und nach einem harten Neuladen (Umschalt+F5) ebenfalls nicht. navigator.serviceWorker.controller ist dann null. Nutze Optional Chaining oder warte auf navigator.serviceWorker.ready.
  • redundant: Dieser Zustand bedeutet, dass der Worker ersetzt wurde oder die Installation fehlgeschlagen ist. Eine solche Referenz ist nicht mehr zu gebrauchen.
  • Nachrichten immer prüfen: Prüfe im Worker event.origin bzw. event.source und das Format von event.data. Führe niemals empfangene Daten als Code aus (eval).
  • Nur sicherer Kontext: Die API funktioniert nur über HTTPS oder auf localhost. In manchen privaten Modi fehlt sie, zum Beispiel im privaten Modus von Firefox. Frage sie deshalb immer mit 'serviceWorker' in navigator ab.
  • Kein dauerhafter Zustand im Worker: Der Browser kann den Worker jederzeit beenden. Globale Variablen darin gehen dann verloren. Speichere Daten stattdessen in IndexedDB oder in der Cache API.
  • Die Form postMessage(message, { transfer }) mit Options-Objekt unterstützen neuere Browser. Die Array-Form funktioniert überall.