Workbox-Hintergrundsynchronisierung

Wenn Sie Daten an einen Webserver senden, schlagen die Anfragen manchmal fehl. Möglicherweise hat der Nutzer die Verbindung verloren oder der Server ist ausgefallen. In beiden Fällen möchten Sie häufig versuchen, die Anfragen später noch einmal zu senden.

Die neue BackgroundSync API ist eine ideale Lösung für dieses Problem. Wenn ein Service Worker erkennt, dass eine Netzwerkanfrage fehlgeschlagen ist, kann er sich für den Empfang eines sync-Ereignisses registrieren. Dieses Ereignis wird gesendet, wenn der Browser denkt, dass die Verbindung wiederhergestellt wurde. Beachten Sie, dass das Synchronisierungsereignis selbst dann gesendet werden kann, wenn der Nutzer die Anwendung verlassen hat, was es wesentlich effektiver macht als die herkömmliche Methode zum Wiederholen fehlgeschlagener Anfragen.

Die Workbox-Hintergrundsynchronisierung soll die Nutzung der BackgroundSync API und die Einbindung in andere Workbox-Module vereinfachen. Außerdem wird eine Fallback-Strategie für Browser implementiert, die BackgroundSync noch nicht implementieren.

Browser, die die BackgroundSync API unterstützen, geben fehlgeschlagene Anfragen automatisch in Ihrem Namen in einem vom Browser verwalteten Intervall wieder. Dabei wird wahrscheinlich ein exponentieller Backoff zwischen Wiederholungsversuchen verwendet. In Browsern, die die BackgroundSync API nicht nativ unterstützen, versucht die Workbox-Hintergrundsynchronisierung automatisch, bei jedem Start des Service Workers eine erneute Wiedergabe durchzuführen.

Grundlegende Nutzung

Die einfachste Methode zur Verwendung der Hintergrundsynchronisierung besteht darin, Plugin zu verwenden. Damit werden fehlgeschlagene Anfragen automatisch in die Warteschlange gestellt und wiederholt, wenn zukünftige sync-Ereignisse ausgelöst werden.

import {BackgroundSyncPlugin} from 'workbox-background-sync';
import {registerRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {
  maxRetentionTime: 24 * 60, // Retry for max of 24 Hours (specified in minutes)
});

registerRoute(
  /\/api\/.*\/*.json/,
  new NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
  'POST'
);

BackgroundSyncPlugin bindet sich in den Callback des Plug-ins fetchDidFail. fetchDidFail wird nur aufgerufen, wenn eine Ausnahme ausgelöst wird. Dies ist höchstwahrscheinlich auf einen Netzwerkfehler zurückzuführen. Anfragen werden also nicht wiederholt, wenn eine Antwort mit dem Fehlerstatus 4xx oder 5xx empfangen wird. Wenn Sie alle Anfragen wiederholen möchten, die z.B. zum Status 5xx führen, können Sie Ihrer Strategie ein fetchDidSucceed-Plug-in hinzufügen:

const statusPlugin = {
  fetchDidSucceed: ({response}) => {
    if (response.status >= 500) {
      // Throwing anything here will trigger fetchDidFail.
      throw new Error('Server error.');
    }
    // If it's not 5xx, use the response as-is.
    return response;
  },
};

// Add statusPlugin to the plugins array in your strategy.

Erweiterte Nutzung

Die Workbox Background Sync bietet auch eine Queue-Klasse, die Sie instanziieren und ihr fehlgeschlagene Anfragen hinzufügen können. Die fehlgeschlagenen Anfragen werden in IndexedDB gespeichert und wiederholt, wenn der Browser denkt, dass die Verbindung wiederhergestellt ist, d.h. wenn er das Synchronisierungsereignis empfängt.

Warteschlange erstellen

Um eine Workbox-Hintergrundsynchronisierungswarteschlange zu erstellen, müssen Sie sie mit einem Warteschlangennamen erstellen, der für Ihren Ursprung eindeutig sein muss:

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

Der Warteschlangenname wird als Teil des Tag-Namens verwendet, der vom globalen SyncManager-Element an register() übergeben wird. Sie wird auch als Objektspeichername für die IndexedDB-Datenbank verwendet.

Anfrage zur Warteschlange hinzufügen

Nachdem Sie die Warteschlangeninstanz erstellt haben, können Sie ihr fehlgeschlagene Anfragen hinzufügen. Zum Hinzufügen einer fehlgeschlagenen Anfrage rufen Sie die Methode .pushRequest() auf. Mit dem folgenden Code werden beispielsweise alle fehlgeschlagenen Anfragen abgefangen und der Warteschlange hinzugefügt:

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

self.addEventListener('fetch', event => {
  // Add in your own criteria here to return early if this
  // isn't a request that should use background sync.
  if (event.request.method !== 'POST') {
    return;
  }

  const bgSyncLogic = async () => {
    try {
      const response = await fetch(event.request.clone());
      return response;
    } catch (error) {
      await queue.pushRequest({request: event.request});
      return error;
    }
  };

  event.respondWith(bgSyncLogic());
});

Nach dem Hinzufügen zur Warteschlange wird die Anfrage automatisch wiederholt, wenn der Service Worker das Ereignis sync empfängt. Das ist der Fall, wenn der Browser feststellt, dass die Verbindung wiederhergestellt ist. Browser, die die BackgroundSync API nicht unterstützen, wiederholen die Warteschlange bei jedem Start des Service Workers noch einmal. Dazu muss die Seite, die den Service Worker steuert, ausgeführt werden, sodass dies nicht ganz so effektiv ist.

Workbox-Hintergrundsynchronisierung wird getestet

Leider ist das Testen von BackgroundSync etwas wenig intuitiv und aus verschiedenen Gründen schwierig.

Gehen Sie am besten so vor, um Ihre Implementierung zu testen:

  1. Laden Sie eine Seite und registrieren Sie Ihren Service Worker.
  2. Schalten Sie das Netzwerk Ihres Computers aus oder Ihren Webserver.
    • Chrome DEVTOOLS NICHT OFFLINE VERWENDEN. Das Offline-Kästchen in den Entwicklertools wirkt sich nur auf Anfragen von der Seite aus. Service Worker-Anfragen werden weiterhin verarbeitet.
  3. Stellen Sie Netzwerkanfragen, die mithilfe der Workbox-Hintergrundsynchronisierung in die Warteschlange gestellt werden sollen.
    • In Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests sehen Sie, ob die Anfragen in die Warteschlange gestellt wurden.
  4. Schalten Sie nun Ihr Netzwerk oder Ihren Webserver ein.
  5. Erzwingen Sie ein frühes sync-Ereignis. Rufen Sie dazu Chrome DevTools > Application > Service Workers auf, geben Sie den Tag-Namen von workbox-background-sync:<your queue name> ein, wobei <your queue name> der Name der von Ihnen festgelegten Warteschlange sein sollte, und klicken Sie dann auf die Schaltfläche „Synchronisieren“.

    Beispiel für die Schaltfläche „Synchronisieren“ in den Chrome-Entwicklertools

  6. Es sollten Netzwerkanfragen für die fehlgeschlagenen Anfragen verarbeitet werden und die IndexedDB-Daten sollten jetzt leer sein, da die Anfragen erfolgreich wiederholt wurden.

Typen

BackgroundSyncPlugin

Eine Klasse, die den Lebenszyklus-Callback fetchDidFail implementiert. Dies vereinfacht das Hinzufügen fehlgeschlagener Anfragen zu einer Warteschlange für die Hintergrundsynchronisierung.

Attribute

Queue

Eine Klasse, die das Speichern fehlgeschlagener Anfragen in IndexedDB verwaltet und diese später wiederholt. Alle Teile des Speicher- und Wiederholungsprozesses können über Callbacks beobachtet werden.

Attribute

  • Konstruktor

    void

    Erstellt eine Queue-Instanz mit den angegebenen Optionen

    Die Funktion constructor sieht so aus:

    (name: string,options?: QueueOptions)=> {...}

    • name

      String

      Der eindeutige Name für diese Warteschlange. Dieser Name muss eindeutig sein, da er zum Registrieren von Synchronisierungsereignissen und zum Speichern von Anfragen in der für diese Instanz spezifischen IndexedDB verwendet wird. Wird ein doppelter Name erkannt, wird eine Fehlermeldung ausgegeben.

    • Optionen

      QueueOptions optional

  • name

    String

  • getAll

    void

    Gibt alle Einträge zurück, die nicht abgelaufen sind (gemäß maxRetentionTime). Abgelaufene Einträge werden aus der Warteschlange entfernt.

    Die Funktion getAll sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<QueueEntry[]>

  • popRequest

    void

    Entfernt die letzte Anfrage in der Warteschlange und gibt sie zurück (zusammen mit ihrem Zeitstempel und allen Metadaten). Das zurückgegebene Objekt hat das Format {request, timestamp, metadata}.

    Die Funktion popRequest sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<QueueEntry>

  • pushRequest

    void

    Speichert die übergebene Anfrage am Ende der Warteschlange in IndexedDB (mit Zeitstempel und Metadaten).

    Die Funktion pushRequest sieht so aus:

    (entry: QueueEntry)=> {...}

    • Eintrag

      QueueEntry

    • Gibt zurück

      Promise<void>

  • registerSync

    void

    Registriert ein Synchronisierungsereignis mit einem für diese Instanz eindeutigen Tag.

    Die Funktion registerSync sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<void>

  • replayRequests

    void

    Durchläuft jede Anfrage in der Warteschlange und versucht, sie noch einmal abzurufen. Wenn eine Anfrage nicht noch einmal abgerufen werden kann, wird sie an dieselbe Position in der Warteschlange verschoben (wodurch eine Wiederholung für das nächste Synchronisierungsereignis registriert wird).

    Die Funktion replayRequests sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<void>

  • shiftRequest

    void

    Entfernt die erste Anfrage (zusammen mit ihrem Zeitstempel und allen Metadaten) aus der Warteschlange und gibt sie zurück. Das zurückgegebene Objekt hat das Format {request, timestamp, metadata}.

    Die Funktion shiftRequest sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<QueueEntry>

  • Größe

    void

    Gibt die Anzahl der in der Warteschlange vorhandenen Einträge zurück. Beachte, dass abgelaufene Einträge (pro maxRetentionTime) auch in dieser Anzahl enthalten sind.

    Die Funktion size sieht so aus:

    ()=> {...}

    • Gibt zurück

      Versprechen<Zahl>

  • unshiftRequest

    void

    Speichert die übergebene Anfrage mit ihrem Zeitstempel und etwaigen Metadaten in IndexedDB am Anfang der Warteschlange.

    Die Funktion unshiftRequest sieht so aus:

    (entry: QueueEntry)=> {...}

    • Eintrag

      QueueEntry

    • Gibt zurück

      Promise<void>

QueueOptions

Attribute

  • forceSyncFallback

    Boolescher Wert optional

  • maxRetentionTime

    Nummer optional

  • onSync

    OnSyncCallback optional

QueueStore

Eine Klasse zum Verwalten des Speicherns von Anfragen aus einer Warteschlange in IndexedDB, die für einen leichteren Zugriff nach ihrem Warteschlangennamen indexiert wird.

Die meisten Entwickler müssen nicht direkt auf diese Klasse zugreifen. Sie ist für erweiterte Anwendungsfälle verfügbar.

Attribute

  • Konstruktor

    void

    Ordnet diese Instanz einer Warteschlangeninstanz zu, sodass hinzugefügte Einträge anhand ihres Warteschlangennamens identifiziert werden können.

    Die Funktion constructor sieht so aus:

    (queueName: string)=> {...}

    • queueName

      String

  • deleteEntry

    void

    Löscht den Eintrag für die angegebene ID.

    WARNUNG: Mit dieser Methode wird nicht sichergestellt, dass der gelöschte Eintrag zu dieser Warteschlange gehört (d.h., er entspricht dem queueName). Diese Einschränkung ist jedoch akzeptabel, da diese Klasse nicht öffentlich zugänglich ist. Eine zusätzliche Prüfung würde diese Methode langsamer machen, als sie sein muss.

    Die Funktion deleteEntry sieht so aus:

    (id: number)=> {...}

    • id

      Zahl

    • Gibt zurück

      Promise<void>

  • getAll

    void

    Gibt alle Einträge im Speicher zurück, die mit queueName übereinstimmen.

    Die Funktion getAll sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<QueueStoreEntry[]>

  • popEntry

    void

    Entfernt den letzten Eintrag in der Warteschlange, der mit queueName übereinstimmt, und gibt ihn zurück.

    Die Funktion popEntry sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<QueueStoreEntry>

  • pushEntry

    void

    Hängen Sie einen Eintrag an das letzte Mal in der Warteschlange an.

    Die Funktion pushEntry sieht so aus:

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • Eintrag

      UnidentifiedQueueStoreEntry

    • Gibt zurück

      Promise<void>

  • shiftEntry

    void

    Entfernt den ersten Eintrag in der Warteschlange, der mit queueName übereinstimmt, und gibt ihn zurück.

    Die Funktion shiftEntry sieht so aus:

    ()=> {...}

    • Gibt zurück

      Promise<QueueStoreEntry>

  • Größe

    void

    Gibt die Anzahl der Einträge im Speicher zurück, die mit queueName übereinstimmen.

    Die Funktion size sieht so aus:

    ()=> {...}

    • Gibt zurück

      Versprechen<Zahl>

  • unshiftEntry

    void

    Stellen Sie in der Warteschlange einen Eintrag zuerst voran.

    Die Funktion unshiftEntry sieht so aus:

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • Eintrag

      UnidentifiedQueueStoreEntry

    • Gibt zurück

      Promise<void>

StorableRequest

Eine Klasse, die das Serialisieren und Deserialisieren von Anfragen vereinfacht, damit sie in IndexedDB gespeichert werden können.

Die meisten Entwickler müssen nicht direkt auf diese Klasse zugreifen. Sie ist für erweiterte Anwendungsfälle verfügbar.

Attribute

  • Konstruktor

    void

    Akzeptiert ein Objekt mit Anfragedaten, die zum Erstellen eines Request verwendet werden können, aber auch in IndexedDB gespeichert werden können.

    Die Funktion constructor sieht so aus:

    (requestData: RequestData)=> {...}

    • requestData

      RequestData

      Ein Objekt mit Anfragedaten, das url sowie alle relevanten Attribute von [requestInit]https://fetch.spec.whatwg.org/#requestinit enthält.

  • clone

    void

    Erstellt einen Deep-Klon der Instanz und gibt ihn zurück.

    Die Funktion clone sieht so aus:

    ()=> {...}

  • toObject

    void

    Gibt einen Deep-Klon des _requestData-Instanzobjekts zurück.

    Die Funktion toObject sieht so aus:

    ()=> {...}

    • Gibt zurück

      RequestData

  • toRequest

    void

    Konvertiert diese Instanz in eine Anfrage.

    Die Funktion toRequest sieht so aus:

    ()=> {...}

    • Gibt zurück

      Anfragen

  • fromRequest

    void

    Wandelt ein Anfrageobjekt in ein einfaches Objekt um, das strukturiert oder als JSON-String formatiert werden kann.

    Die Funktion fromRequest sieht so aus:

    (request: Request)=> {...}

    • Request

      Anfragen