Start · Sprachen · JavaScript · Referenz · ExtendableEvent.waitUntil()

ExtendableEvent.waitUntil()

Methode

Verlängert die Lebensdauer eines Service-Worker-Ereignisses, bis das übergebene <code>Promise</code> erfüllt oder abgelehnt ist.

seit JavaScript Baseline: alle modernen Browser (Service Worker) Kategorie: webapi

Signatur

ExtendableEvent.prototype.waitUntil(promise)

Beschreibung

waitUntil() teilt dem Browser mit, dass nach dem Ereignis noch Arbeit läuft. Solange das übergebene Promise nicht abgeschlossen ist, beendet der Browser den Service Worker nicht. Ohne diesen Aufruf darf der Browser den Worker direkt nach dem synchronen Teil des Event-Handlers stoppen. Asynchrone Vorgänge wie das Befüllen des Caches würden dann mittendrin abbrechen.

Die wichtigsten Einsatzorte sind:

  • install: Dateien vorab in den Cache laden (Precaching). Wird das Promise abgelehnt, gilt die Installation als fehlgeschlagen und der Worker wird verworfen.
  • activate: veraltete Caches löschen oder Daten migrieren. Funktionale Ereignisse wie fetch werden erst zugestellt, wenn das Promise abgeschlossen ist.
  • push, notificationclick, sync, message: Benachrichtigungen anzeigen, Fenster öffnen oder Daten im Hintergrund synchronisieren.

Man kann waitUntil() mehrmals aufrufen. Der Browser wartet dann, bis alle übergebenen Promises abgeschlossen sind. Bei install führt schon ein einziges abgelehntes Promise dazu, dass die Installation fehlschlägt.

Beim fetch-Ereignis (ein FetchEvent, das von ExtendableEvent erbt) verzögert waitUntil() nicht die Antwort. Es hält den Worker nur für Hintergrundarbeit am Leben, etwa um den Cache nach der Antwort zu aktualisieren. Für die Antwort selbst ist respondWith() zuständig.

Parameter

Name Typ Default Beschreibung
$promise Pflicht Promise<any> Ein Promise, das die laufende Arbeit darstellt. Der Browser hält den Worker am Leben, bis es erfüllt oder abgelehnt ist. Wird kein Promise übergeben, wird der Wert wie mit Promise.resolve() in ein Promise umgewandelt.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert (undefined).

Beispiele

Precaching im install-Ereignis

// sw.js
const CACHE_NAME = 'app-v2';
const ASSETS = ['/', '/index.html', '/styles.css', '/app.js', '/logo.svg'];

self.addEventListener('install', (event) => {
  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE_NAME);
      await cache.addAll(ASSETS); // schlägt ein Request fehl, schlägt die Installation fehl
      console.log('Precaching abgeschlossen');
    })()
  );
});
Precaching abgeschlossen

Alte Caches im activate-Ereignis aufräumen

const CURRENT_CACHES = ['app-v2'];

self.addEventListener('activate', (event) => {
  event.waitUntil(
    (async () => {
      const keys = await caches.keys();
      await Promise.all(
        keys
          .filter((key) => !CURRENT_CACHES.includes(key))
          .map((key) => caches.delete(key))
      );
      await self.clients.claim(); // sofort Kontrolle über offene Seiten übernehmen
      console.log('Alte Caches entfernt:', keys.filter((k) => !CURRENT_CACHES.includes(k)));
    })()
  );
});
Alte Caches entfernt: ["app-v1"]

Push-Benachrichtigung anzeigen

self.addEventListener('push', (event) => {
  const data = event.data?.json() ?? { title: 'Neuigkeiten', body: 'Es gibt etwas Neues.' };

  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icons/icon-192.png',
    })
  );
});

Stale-While-Revalidate: Cache im fetch-Ereignis im Hintergrund aktualisieren

self.addEventListener('fetch', (event) => {
  if (event.request.method !== 'GET') return;

  event.respondWith(
    (async () => {
      const cache = await caches.open('runtime');
      const cached = await cache.match(event.request);

      const networkUpdate = fetch(event.request).then(async (response) => {
        if (response.ok) await cache.put(event.request, response.clone());
        return response;
      });

      // Worker am Leben halten, bis der Cache aktualisiert ist
      event.waitUntil(networkUpdate.catch(() => {}));

      return cached ?? networkUpdate;
    })()
  );
});

// Wichtig · Fallstricke

  • Synchron aufrufen: waitUntil() muss während der Ereigniszustellung aufgerufen werden, also im synchronen Teil des Handlers oder solange ein bereits übergebenes Promise noch offen ist. Ein späterer Aufruf, etwa nach einem await ohne vorheriges waitUntil(), wirft einen InvalidStateError (DOMException).
  • Kein async-Handler als Ersatz: Ein async-Event-Listener allein hält den Worker nicht am Leben. Den Browser interessiert das vom Listener zurückgegebene Promise nicht. Die asynchrone Arbeit muss deshalb immer an waitUntil() übergeben werden.
  • Ablehnung bei install: Ein abgelehntes Promise lässt die Installation scheitern. cache.addAll() ist atomar: Ist auch nur eine einzige URL nicht erreichbar (z. B. 404), schlägt die gesamte Installation fehl.
  • Zeitlimits: Browser setzen eigene Obergrenzen für die Lebensdauer eines Workers, typischerweise wenige Minuten. Sehr lange Aufgaben können trotz waitUntil() abgebrochen werden. Für große Übertragungen eignen sich Background Sync oder Background Fetch.
  • Kompatibilität: Nur in Service-Worker-Kontexten verfügbar (Chrome, Edge, Firefox, Safari 11.1+). Service Worker setzen HTTPS voraus (Ausnahme: localhost). Im privaten Modus von Firefox sind Service Worker nicht verfügbar.