Présentation de la récupération en arrière-plan

Jake Archibald
Jake Archibald

En 2015, nous avons lancé la synchronisation en arrière-plan, qui permet pour différer le travail jusqu'à ce que l'utilisateur dispose d'une connectivité. Cela signifie que l'utilisateur peut saisir puis cliquez sur "Envoyer" et quittez le site en sachant que le message sera envoyé immédiatement ou lorsqu'il doivent être connectés.

Il s'agit d'une fonctionnalité utile, mais elle nécessite que le service worker soit actif pendant la durée de récupérer. Ce n'est pas un problème pour des tâches courtes comme l'envoi d'un message, mais si la tâche prend pendant trop longtemps, le navigateur tuera le service worker. Sinon, il s'agit d'un risque pour la confidentialité de la batterie.

Que se passe-t-il si vous avez besoin de télécharger un contenu qui peut prendre beaucoup de temps, comme un film, un podcast ou différents niveaux d'un jeu. C'est à cela que sert la récupération en arrière-plan.

La récupération en arrière-plan est disponible par défaut depuis Chrome 74.

Voici une démonstration rapide de deux minutes qui illustre l'état classique des choses par rapport à l'utilisation de la récupération en arrière-plan:

Essayez la démo et parcourez le code.

Fonctionnement

La récupération en arrière-plan fonctionne comme suit:

  1. Vous demandez au navigateur d'effectuer un groupe d'extractions en arrière-plan.
  2. Le navigateur récupère ces éléments et présente la progression à l'utilisateur.
  3. Une fois la récupération terminée ou échouée, le navigateur ouvre votre service worker et déclenche un événement pour vous expliquer ce qui s'est passé. C'est ici que vous décidez de ce que vous souhaitez faire des réponses, le cas échéant.

Si l'utilisateur ferme des pages de votre site après l'étape 1, le téléchargement se poursuit. En effet, que l'extraction est très visible et facilement annulable, cela ne pose pas de problème de confidentialité de synchronisation en arrière-plan. Comme le service worker n'est pas constamment en cours d'exécution, il n'est pas nécessaire qu'il pourrait abuser du système, comme miner des bitcoins en arrière-plan.

Sur certaines plates-formes (comme Android), le navigateur peut se fermer après l'étape 1, car le navigateur peut transmettre l'extraction au système d'exploitation.

Si l'utilisateur lance le téléchargement hors connexion ou passe hors connexion pendant le téléchargement, l'arrière-plan la récupération sera suspendue et réactivée ultérieurement.

L'API

Détection de caractéristiques

Comme pour toute nouvelle fonctionnalité, vous devez vérifier si le navigateur la prend en charge. Pour la récupération en arrière-plan, C'est aussi simple que cela:

if ('BackgroundFetchManager' in self) {
  // This browser supports Background Fetch!
}

Lancer une récupération en arrière-plan

L'API principale bloque l'enregistrement d'un service worker. Assurez-vous donc d'avoir d'abord enregistré un service worker. Ensuite :

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch('my-fetch', ['/ep-5.mp3', 'ep-5-artwork.jpg'], {
    title: 'Episode 5: Interesting things.',
    icons: [{
      sizes: '300x300',
      src: '/ep-5-icon.png',
      type: 'image/png',
    }],
    downloadTotal: 60 * 1024 * 1024,
  });
});

backgroundFetch.fetch comporte trois arguments:

Paramètres
id string
identifie de manière unique cette récupération en arrière-plan.

backgroundFetch.fetch refusera si l'ID correspond à un arrière-plan existant récupérer.

requests Array<Request|string>
Les éléments à récupérer. Les chaînes seront traitées comme des URL et transformées en Request via new Request(theString).

Vous pouvez récupérer des éléments d'autres origines, à condition que les ressources le permettent via CORS

Remarque:Pour le moment, Chrome ne prend pas en charge les requêtes une requête CORS préliminaire.

options Un objet pouvant inclure les éléments suivants:
options.title string
Titre à afficher pour le navigateur avec la progression.
options.icons Array<IconDefinition>
Tableau d'objets avec un "src", une "size" et un "type".
options.downloadTotal number
Taille totale des corps de la réponse (après décompression au format gzip).

Bien que cette étape soit facultative, nous vous recommandons vivement de la fournir. Il est utilisé pour indiquer à l'utilisateur la taille du téléchargement et de fournir des informations sur la progression. Si vous ne fournissez pas le navigateur indique à l'internaute que la taille est inconnue, ce qui peut lui demander risque d'annuler le téléchargement.

Si les téléchargements de la récupération en arrière-plan dépassent le nombre indiqué ici, ils seront annulés. Il est est tout à fait normal si le téléchargement est inférieur à downloadTotal. Si vous n'êtes pas s'assurer du nombre total de téléchargements, préférez la prudence.

backgroundFetch.fetch renvoie une promesse qui se résout avec un BackgroundFetchRegistration. Je vais nous en parlerons en détail plus tard. La promesse est refusée si l'utilisateur a désactivé les téléchargements, des paramètres fournis n'est pas valide.

Fournir plusieurs requêtes pour une seule récupération en arrière-plan vous permet de combiner des éléments qui sont logiquement pour l'utilisateur. Par exemple, un film peut être divisé en milliers de ressources MPEG-DASH), et sont fournis avec des ressources supplémentaires comme des images. Un niveau d'un jeu peut être réparti sur plusieurs JavaScript, image et ressources audio. Mais pour l'utilisateur, il ne s'agit que de "le film" ou "le niveau".

Obtenir une récupération en arrière-plan existante

Vous pouvez récupérer une extraction en arrière-plan existante comme ceci:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});

... en transmettant l'id de l'extraction en arrière-plan souhaitée. get renvoie undefined s'il n'y a pas de une récupération active en arrière-plan associée à cet ID.

Une récupération en arrière-plan est considérée comme "active" entre son enregistrement et la réussite de l'opération, échoue ou est annulée.

Vous pouvez obtenir la liste de toutes les récupérations actives en arrière-plan à l'aide de getIds:

navigator.serviceWorker.ready.then(async (swReg) => {
  const ids = await swReg.backgroundFetch.getIds();
});

Enregistrements de récupération en arrière-plan

Un élément BackgroundFetchRegistration (bgFetch dans les exemples ci-dessus) présente les caractéristiques suivantes:

Propriétés
id string
ID de la récupération en arrière-plan.
uploadTotal number
Nombre d'octets à envoyer au serveur.
uploaded number
Nombre d'octets envoyés.
downloadTotal number
Valeur fournie lors de l'enregistrement de la récupération en arrière-plan, ou zéro.
downloaded number
Nombre d'octets reçus.

Cette valeur peut diminuer. Par exemple, si la connexion est interrompue et que le téléchargement ne peut pas Dans ce cas, le navigateur relance la récupération de cette ressource à partir de zéro.

result

Choisissez l'une des options suivantes :

  • "" : la récupération en arrière-plan est active. Aucun résultat n'est donc disponible pour le moment.
  • "success" : la récupération de l'arrière-plan a réussi.
  • "failure" : échec de la récupération en arrière-plan. Cette valeur n'apparaît que si la récupération en arrière-plan échoue totalement, car le navigateur ne peut pas réessayer/reprendre.
failureReason

Choisissez l'une des options suivantes :

  • "" : la récupération en arrière-plan n'a pas échoué.
  • "aborted" : l'utilisateur a annulé la récupération en arrière-plan. abort() a été appelé.
  • "bad-status" : l'une des réponses présentait un état incorrect (par exemple, Erreur 404.
  • "fetch-error" : l'une des récupérations a échoué pour une autre raison, par exemple CORS, MIX, une réponse partielle non valide ou un échec réseau général pour une récupération qui ne peut pas faire l'objet d'une nouvelle tentative.
  • "quota-exceeded" : le quota de stockage a été atteint en arrière-plan récupérer.
  • "download-total-exceeded" : la valeur "downloadTotal" fournie était dépassé.
recordsAvailable boolean
Les requêtes/réponses sous-jacentes sont-elles accessibles ?

Si la valeur est "false", match et matchAll ne peut plus être utilisé.

Méthodes
abort() Renvoie Promise<boolean>
Annule la récupération en arrière-plan.

La promesse renvoyée se résout avec "true" si la récupération a été annulée avec succès.

matchAll(request, opts) Renvoie Promise<Array<BackgroundFetchRecord>>
Obtenir les requêtes et ses réponses.

Les arguments ici sont les mêmes que le cache API. Un appel sans arguments renvoie une promesse pour tous les enregistrements.

Pour en savoir plus, reportez-vous aux informations ci-dessous.

match(request, opts) Affiche Promise<BackgroundFetchRecord>
Comme ci-dessus, mais se résout avec la première correspondance.
Événements
progress Déclenché lorsque uploaded, downloaded, result ou Variation de failureReason.

Suivi de la progression

Pour ce faire, utilisez l'événement progress. Rappelez-vous que downloadTotal est la valeur que vous ou 0 si vous n'avez pas fourni de valeur.

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(bgFetch.downloaded / bgFetch.downloadTotal * 100);
  console.log(`Download progress: ${percent}%`);
});

Obtenir les requêtes et les réponses

bgFetch.match('/ep-5.mp3').then(async (record) => {
  if (!record) {
    console.log('No record found');
    return;
  }

  console.log(`Here's the request`, record.request);
  const response = await record.responseReady;
  console.log(`And here's the response`, response);
});

record est un BackgroundFetchRecord et ressemble à ceci:

Propriétés
request Request
Requête fournie.
responseReady Promise<Response>
Réponse récupérée.

La réponse sous-tend une promesse, car elle n'a peut-être pas encore été reçue. La promesse rejettera si la récupération échoue.

Événements de service worker

Événements
backgroundfetchsuccess Toutes les données ont bien été récupérées.
backgroundfetchfailure Échec d'une ou de plusieurs récupérations.
backgroundfetchabort Échec d'une ou de plusieurs récupérations.

Ceci n'est vraiment utile que si vous souhaitez effectuer un nettoyage de données connexes.

backgroundfetchclick L'utilisateur a cliqué sur l'interface utilisateur de progression du téléchargement.

Les objets d'événement présentent les caractéristiques suivantes:

Propriétés
registration BackgroundFetchRegistration
Méthodes
updateUI({ title, icons }) permet de modifier le titre et les icônes définis initialement. C'est facultatif, mais cela vous permet fournir plus de contexte si nécessaire. Vous ne pouvez effectuer cette opération qu'*une seule fois* Événements backgroundfetchsuccess et backgroundfetchfailure.

Réagir en cas de réussite ou d'échec

Nous avons déjà vu l'événement progress, mais il n'est utile que lorsque l'utilisateur a ouvert une page sur sur votre site. Le principal avantage de la récupération en arrière-plan est que tout fonctionne une fois que l'utilisateur a quitté ou ferme le navigateur.

Si la récupération en arrière-plan réussit, votre service worker recevra backgroundfetchsuccess, et event.registration sera l'enregistrement de la récupération en arrière-plan.

Après cet événement, les requêtes et les réponses récupérées ne sont plus accessibles. Par conséquent, si vous souhaitez les conserver, déplacez-les par exemple dans l'API cache.

Comme pour la plupart des événements de service worker, utilisez event.waitUntil afin qu'ils sachent quand l'événement est terminé.

Par exemple, dans votre service worker:

addEventListener('backgroundfetchsuccess', (event) => {
  const bgFetch = event.registration;

  event.waitUntil(async function() {
    // Create/open a cache.
    const cache = await caches.open('downloads');
    // Get all the records.
    const records = await bgFetch.matchAll();
    // Copy each request/response across.
    const promises = records.map(async (record) => {
      const response = await record.responseReady;
      await cache.put(record.request, response);
    });

    // Wait for the copying to complete.
    await Promise.all(promises);

    // Update the progress notification.
    event.updateUI({ title: 'Episode 5 ready to listen!' });
  }());
});

L'échec est peut-être dû à une seule erreur 404, qui n'était peut-être pas importante pour vous. valent la peine de copier certaines réponses dans un cache, comme ci-dessus.

Réaction au clic

L'interface utilisateur affichant la progression et le résultat du téléchargement est cliquable. L'événement backgroundfetchclick de le service worker vous permet de réagir. Comme indiqué ci-dessus, event.registration sera l'arrière-plan récupérer l'enregistrement.

L'action la plus courante dans cet événement est d'ouvrir une fenêtre:

addEventListener('backgroundfetchclick', (event) => {
  const bgFetch = event.registration;

  if (bgFetch.result === 'success') {
    clients.openWindow('/latest-podcasts');
  } else {
    clients.openWindow('/download-progress');
  }
});

Ressources supplémentaires

Correction: une version précédente de cet article mentionnait à tort la récupération de l'arrière-plan comme une "norme Web". L'API n'est pas encore en phase de normalisation. La spécification est disponible dans WICG sous la forme d'un projet de rapport de groupe communautaire.