synchronisation en arrière-plan de la boîte de travail

Lorsque vous envoyez des données à un serveur Web, les requêtes peuvent parfois échouer. Cela peut être dû au fait que l'utilisateur a perdu la connectivité ou que le serveur est en panne. Dans les deux cas, vous pouvez réessayer d'envoyer les requêtes plus tard.

La nouvelle API BackgroundSync est une solution idéale à ce problème. Lorsqu'un service worker détecte l'échec d'une requête réseau, il peut s'inscrire pour recevoir un événement sync, qui est envoyé lorsque le navigateur pense que la connectivité est renvoyée. Notez que l'événement de synchronisation peut être diffusé même si l'utilisateur a quitté l'application, ce qui est beaucoup plus efficace que la méthode traditionnelle qui consiste à relancer les requêtes ayant échoué.

La synchronisation en arrière-plan de Workbox est conçue pour faciliter l'utilisation de l'API BackgroundSync et l'intégrer à d'autres modules Workbox. Elle implémente également une stratégie de remplacement pour les navigateurs qui n'implémentent pas encore BackgroundSync.

Les navigateurs compatibles avec l'API BackgroundSync relancent automatiquement les requêtes ayant échoué en votre nom à un intervalle géré par le navigateur, probablement à l'aide d'un intervalle exponentiel entre les tentatives. Dans les navigateurs qui ne sont pas compatibles de manière native avec l'API BackgroundSync, Workbox Background Sync tente automatiquement de la relancer au démarrage du service worker.

Utilisation de base

Le moyen le plus simple d'utiliser la synchronisation en arrière-plan consiste à utiliser Plugin, qui mettra automatiquement en file d'attente les requêtes ayant échoué et les relancera lorsque de futurs événements sync seront déclenchés.

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 se connecte au rappel du plug-in fetchDidFail, et fetchDidFail n'est appelé que si une exception est générée, très probablement en raison d'une défaillance du réseau. Cela signifie que les requêtes ne seront pas relancées si une réponse présente l'état d'erreur 4xx ou 5xx. Si vous souhaitez relancer toutes les requêtes qui aboutissent, par exemple à l'état 5xx, vous pouvez le faire en ajoutant un plug-in fetchDidSucceed à votre stratégie:

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.

Utilisation avancée

La synchronisation en arrière-plan de Workbox fournit également une classe Queue, à laquelle vous pouvez instancier et ajouter les requêtes ayant échoué. Les requêtes ayant échoué sont stockées dans IndexedDB et font l'objet d'une nouvelle tentative lorsque le navigateur pense que la connectivité a été restaurée (c'est-à-dire lorsqu'il reçoit l'événement de synchronisation).

Créer une file d'attente

Pour créer une file d'attente de synchronisation en arrière-plan d'une boîte de travail, vous devez la construire avec un nom de file d'attente (qui doit être unique à votre origine):

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

const queue = new Queue('myQueueName');

Le nom de la file d'attente est utilisé dans le nom du tag qui est ajouté à register() par le SyncManager global. Il est également utilisé comme nom de magasin d'objets pour la base de données IndexedDB.

Ajouter une requête à la file d'attente

Une fois que vous avez créé votre instance Queue, vous pouvez y ajouter des requêtes ayant échoué. Pour ajouter une requête ayant échoué, appelez la méthode .pushRequest(). Par exemple, le code suivant détecte toutes les requêtes qui échouent et les ajoute à la file d'attente:

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());
});

Une fois ajoutée à la file d'attente, la requête fait automatiquement l'objet d'une nouvelle tentative lorsque le service worker reçoit l'événement sync (qui se produit lorsque le navigateur pense que la connectivité est restaurée). Les navigateurs qui ne sont pas compatibles avec l'API BackgroundSync relancent la file d'attente à chaque démarrage du service worker. Cette approche nécessite l'exécution de la page contrôlant le service worker. Elle ne sera donc pas aussi efficace.

Test de la synchronisation en arrière-plan de la boîte de travail

Malheureusement, tester BackgroundSync est quelque peu peu intuitif et difficile pour plusieurs raisons.

La meilleure approche pour tester votre implémentation consiste à procéder comme suit:

  1. Chargez une page et enregistrez votre service worker.
  2. Désactivez le réseau de votre ordinateur ou votre serveur Web.
    • N'UTILISEZ PAS CHROME DEVTOOLS HORS CONNEXION. La case à cocher hors connexion dans DevTools n'affecte que les requêtes provenant de la page. Les requêtes de service worker continueront d'être traitées.
  3. Effectuez des requêtes réseau qui doivent être placées en file d'attente à l'aide de la synchronisation en arrière-plan Workbox.
    • Vous pouvez vérifier que les requêtes ont été mises en file d'attente dans Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests.
  4. Activez maintenant votre réseau ou votre serveur Web.
  5. Forcez un événement sync anticipé en accédant à Chrome DevTools > Application > Service Workers, en saisissant le nom de balise workbox-background-sync:<your queue name>, où <your queue name> doit être le nom de la file d'attente que vous avez définie, puis en cliquant sur le bouton "Synchroniser".

    Exemple de bouton de synchronisation dans les outils pour les développeurs Chrome

  6. Vous devriez voir les requêtes réseau aboutir pour les requêtes ayant échoué et les données IndexedDB devraient être vides puisque les requêtes ont bien été rejouées.

Types

BackgroundSyncPlugin

Une classe implémentant le rappel de cycle de vie fetchDidFail. Il est ainsi plus facile d'ajouter les requêtes ayant échoué à une file d'attente de synchronisation en arrière-plan.

Propriétés

Queue

Une classe permettant de gérer le stockage des requêtes ayant échoué dans IndexedDB et de les réessayer ultérieurement. Toutes les parties du processus de stockage et de relecture sont observables via des rappels.

Propriétés

  • constructor

    void

    Crée une instance de Queue avec les options données.

    La fonction constructor se présente comme suit :

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

    • name

      chaîne

      Nom unique de cette file d'attente. Ce nom doit être unique, car il permet d'enregistrer des événements de synchronisation et de stocker des requêtes dans IndexedDB spécifiques à cette instance. Une erreur est générée si un nom en double est détecté.

    • options

      QueueOptions facultatif

  • name

    chaîne

  • getAll

    void

    Renvoie toutes les entrées qui n'ont pas expiré (par maxRetentionTime). Toutes les entrées expirées sont supprimées de la file d'attente.

    La fonction getAll se présente comme suit :

    ()=> {...}

    • retours

      Promise<QueueEntry[]>

  • popRequest

    void

    Supprime et renvoie la dernière requête de la file d'attente (ainsi que son horodatage et ses métadonnées). L'objet renvoyé se présente sous la forme suivante : {request, timestamp, metadata}.

    La fonction popRequest se présente comme suit :

    ()=> {...}

    • retours

      Promise<QueueEntry>

  • pushRequest

    void

    Stocke la requête transmise dans IndexedDB (avec son horodatage et ses métadonnées) à la fin de la file d'attente.

    La fonction pushRequest se présente comme suit :

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

    • entrée

      QueueEntry

    • retours

      Promise<void>

  • registerSync

    void

    Enregistre un événement de synchronisation avec un tag propre à cette instance.

    La fonction registerSync se présente comme suit :

    ()=> {...}

    • retours

      Promise<void>

  • replayRequests

    void

    Elle parcourt chaque requête de la file d'attente et tente de la récupérer à nouveau. Si la récupération d'une requête échoue, elle est remise à la même position dans la file d'attente (ce qui enregistre une nouvelle tentative pour le prochain événement de synchronisation).

    La fonction replayRequests se présente comme suit :

    ()=> {...}

    • retours

      Promise<void>

  • shiftRequest

    void

    Supprimer et renvoyer la première requête de la file d'attente (avec son horodatage et ses métadonnées) L'objet renvoyé se présente sous la forme suivante : {request, timestamp, metadata}.

    La fonction shiftRequest se présente comme suit :

    ()=> {...}

    • retours

      Promise<QueueEntry>

  • taille

    void

    Renvoie le nombre d'entrées présentes dans la file d'attente. Notez que les entrées expirées (par maxRetentionTime) sont également incluses dans ce total.

    La fonction size se présente comme suit :

    ()=> {...}

    • retours

      Promesse<number>

  • unshiftRequest

    void

    Stocke la requête transmise dans IndexedDB (avec son horodatage et ses métadonnées) au début de la file d'attente.

    La fonction unshiftRequest se présente comme suit :

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

    • entrée

      QueueEntry

    • retours

      Promise<void>

QueueOptions

Propriétés

  • forceSyncFallback

    Booléen facultatif

  • maxRetentionTime

    numéro facultatif

  • onSync

    OnSyncCallback facultatif

QueueStore

Une classe permettant de gérer les requêtes de stockage d'une file d'attente dans IndexedDB, indexées par leur nom, pour en faciliter l'accès.

La plupart des développeurs n'ont pas besoin d'accéder directement à cette classe, car elle est exposée pour les cas d'utilisation avancés.

Propriétés

  • constructor

    void

    Associe cette instance à une instance Queue, de sorte que les entrées ajoutées puissent être identifiées par leur nom de file d'attente.

    La fonction constructor se présente comme suit :

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

    • queueName

      chaîne

  • deleteEntry

    void

    Supprime l'entrée correspondant à l'identifiant donné.

    AVERTISSEMENT: Cette méthode ne garantit pas que l'entrée supprimée appartient à cette file d'attente (c'est-à-dire qu'elle correspond à queueName). Toutefois, cette limitation est acceptable, car cette classe n'est pas exposée publiquement. Une vérification supplémentaire rendrait cette méthode plus lente qu'elle ne devrait.

    La fonction deleteEntry se présente comme suit :

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

    • id

      number

    • retours

      Promise<void>

  • getAll

    void

    Renvoie toutes les entrées du magasin correspondant à queueName.

    La fonction getAll se présente comme suit :

    ()=> {...}

    • retours

      Promise<QueueStoreEntry[]>

  • popEntry

    void

    Supprimer et renvoyer la dernière entrée de la file d'attente correspondant à queueName.

    La fonction popEntry se présente comme suit :

    ()=> {...}

    • retours

      Promise<QueueStoreEntry>

  • pushEntry

    void

    Ajoute une entrée en dernier dans la file d'attente.

    La fonction pushEntry se présente comme suit :

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

    • entrée

      UnidentifiedQueueStoreEntry

    • retours

      Promise<void>

  • shiftEntry

    void

    Supprimer et renvoyer la première entrée de la file d'attente correspondant à queueName.

    La fonction shiftEntry se présente comme suit :

    ()=> {...}

    • retours

      Promise<QueueStoreEntry>

  • taille

    void

    Renvoie le nombre d'entrées du magasin correspondant à queueName.

    La fonction size se présente comme suit :

    ()=> {...}

    • retours

      Promesse<number>

  • unshiftEntry

    void

    Ajoutez un préfixe à une entrée dans la file d'attente.

    La fonction unshiftEntry se présente comme suit :

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

    • entrée

      UnidentifiedQueueStoreEntry

    • retours

      Promise<void>

StorableRequest

Classe permettant de faciliter la sérialisation et la désérialisation des requêtes afin qu'elles puissent être stockées dans IndexedDB.

La plupart des développeurs n'ont pas besoin d'accéder directement à cette classe, car elle est exposée pour les cas d'utilisation avancés.

Propriétés

  • constructor

    void

    Accepte un objet de données de requête qui peut être utilisé pour construire un Request, mais qui peut également être stocké dans IndexedDB.

    La fonction constructor se présente comme suit :

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

    • requestData

      RequestData

      Objet des données de requête qui inclut url ainsi que toutes les propriétés pertinentes de [requestInit]https://fetch.spec.whatwg.org/#requestinit.

  • clone

    void

    Crée et affiche un clone profond de l'instance.

    La fonction clone se présente comme suit :

    ()=> {...}

  • toObject

    void

    Renvoie un clone profond de l'objet _requestData des instances.

    La fonction toObject se présente comme suit :

    ()=> {...}

    • retours

      RequestData

  • toRequest

    void

    Convertit cette instance en requête.

    La fonction toRequest se présente comme suit :

    ()=> {...}

    • retours

      Requête

  • fromRequest

    void

    Convertit un objet Request en un objet brut pouvant être structuré, cloné ou concaténé au format JSON.

    La fonction fromRequest se présente comme suit :

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

    • request

      Requête