Start · Sprachen · JavaScript · Referenz · FetchEvent

FetchEvent

Schnittstelle

Ereignistyp für <code>fetch</code>-Ereignisse im Service Worker. Damit lassen sich Netzwerkanfragen abfangen und mit eigenen Antworten beantworten, z. B. aus dem Cache.

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

Signatur

interface FetchEvent extends ExtendableEvent

Beschreibung

FetchEvent ist das Ereignisobjekt, das der Browser im globalen Scope eines Service Workers (ServiceWorkerGlobalScope) auslöst. Das passiert jedes Mal, wenn eine vom Worker kontrollierte Seite eine Ressource anfordert: Dokumente, Skripte, Bilder, CSS oder Aufrufe von fetch(). Das Interface erbt von ExtendableEvent und ist die Grundlage für Offline-Fähigkeit, Caching-Strategien und Progressive Web Apps.

Der zentrale Baustein ist die Methode respondWith(). Ihr übergibt man eine Response oder ein Promise<Response>, und der Browser verwendet diese Antwort statt des normalen Netzwerkergebnisses. Ruft man respondWith() nicht auf, verarbeitet der Browser die Anfrage ganz normal. Mit waitUntil() (geerbt von ExtendableEvent) hält man den Worker aktiv, bis Hintergrundarbeiten wie ein Cache-Update abgeschlossen sind.

Wichtige Eigenschaften:

  • request: das Request-Objekt der abgefangenen Anfrage.
  • clientId: ID des Clients (Fenster/Tab), der die Anfrage stellt.
  • resultingClientId: ID des Clients, der bei Navigationen neu entsteht.
  • preloadResponse: Promise mit der Antwort aus dem Navigation Preload, sofern aktiviert, sonst undefined.
  • handled: Promise, das sich erfüllt, sobald der Browser die Anfrage abgearbeitet hat.

Im Browser erzeugt man FetchEvent praktisch nie selbst, sondern empfängt es über self.addEventListener("fetch", handler) oder self.onfetch. Den Konstruktor new FetchEvent(type, init) gibt es zwar, er ist aber vor allem für Tests gedacht.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Nur beim Konstruktor: der Ereignisname, in der Regel "fetch".
$init Pflicht object Nur beim Konstruktor: Optionsobjekt mit der Pflichtangabe request (Request) und den optionalen Angaben clientId, resultingClientId, preloadResponse, handled sowie den üblichen EventInit-Feldern.
$respondWith(response) Response | Promise<Response> Methode: legt die Antwort fest, die der Browser statt des Netzwerkergebnisses verwendet. Muss synchron im Handler aufgerufen werden.
$waitUntil(promise) Promise<any> Geerbte Methode: verlängert die Lebenszeit des Workers, bis das übergebene Promise abgeschlossen ist, z. B. bei Cache-Updates im Hintergrund.

Rückgabewert

Typ
FetchEvent
Beschreibung
Der Konstruktor gibt ein neues FetchEvent zurück. respondWith() und waitUntil() geben undefined zurück.

Beispiele

Cache-First-Strategie mit Netzwerk-Fallback

// sw.js
self.addEventListener("fetch", (event) => {
  // Nur GET-Anfragen aus dem Cache bedienen
  if (event.request.method !== "GET") return;

  event.respondWith(
    (async () => {
      const cached = await caches.match(event.request);
      if (cached) return cached;
      return fetch(event.request);
    })()
  );
});

Stale-While-Revalidate mit waitUntil()

// sw.js
const CACHE = "app-v1";

self.addEventListener("fetch", (event) => {
  const url = new URL(event.request.url);
  if (!url.pathname.startsWith("/api/")) return;

  const networkPromise = fetch(event.request).then(async (res) => {
    const cache = await caches.open(CACHE);
    await cache.put(event.request, res.clone());
    return res;
  });

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

  event.respondWith(
    caches.match(event.request).then((cached) => cached || networkPromise)
  );
});

Navigation Preload und Offline-Seite

// sw.js
self.addEventListener("activate", (event) => {
  event.waitUntil(self.registration.navigationPreload?.enable());
});

self.onfetch = (event) => {
  if (event.request.mode !== "navigate") return;

  event.respondWith(
    (async () => {
      try {
        const preloaded = await event.preloadResponse;
        if (preloaded) return preloaded;
        return await fetch(event.request);
      } catch {
        // Offline: vorab gecachte Fallback-Seite ausliefern
        return (await caches.match("/offline.html")) ??
          new Response("Offline", { status: 503 });
      }
    })()
  );
};

Eigene Antwort und handled-Promise

// sw.js
self.addEventListener("fetch", (event) => {
  if (new URL(event.request.url).pathname === "/ping") {
    event.respondWith(
      new Response(JSON.stringify({ ok: true, client: event.clientId }), {
        headers: { "Content-Type": "application/json" }
      })
    );
    event.handled.then(() => console.log("/ping beantwortet"));
  }
});
/ping beantwortet

// Wichtig · Fallstricke

  • respondWith() synchron aufrufen: Der Aufruf muss noch während der Ereignisbehandlung erfolgen. Steht er nach einem await, wirft er einen InvalidStateError. Asynchrone Logik gehört deshalb in das übergebene Promise.
  • respondWith() darf pro Ereignis nur einmal aufgerufen werden. Ein zweiter Aufruf löst ebenfalls einen InvalidStateError aus.
  • Body-Streams lassen sich nur einmal lesen. Soll eine Antwort sowohl zurückgegeben als auch gecacht werden, braucht man response.clone().
  • Die Cache API unterstützt cache.put() nur für GET-Anfragen. POST- und andere Anfragen sollte man durchreichen.
  • Opake Antworten (Cross-Origin mit mode: \"no-cors\") haben den Status 0. Beim Cachen belegen sie im Kontingent oft viel Speicher.
  • Sicherheit: Service Worker laufen nur in sicheren Kontexten (HTTPS oder localhost). Wer Antworten zu großzügig cacht, kann Nutzern nach dem Abmelden personalisierte oder veraltete Daten ausliefern. Sensible Endpunkte sollte man daher ausnehmen.
  • Das erste Laden einer Seite ist noch nicht vom Worker kontrolliert. Erst nach der Aktivierung (oder mit clients.claim()) kommen fetch-Ereignisse an.
  • preloadResponse und handled gibt es in allen modernen Browsern, aber erst in neueren Versionen. Vor der Verwendung mit optionaler Verkettung prüfen.