Jetzt neu: Hintergrundabruf

Archibald
Jake Archibald

2015 haben wir die Hintergrundsynchronisierung eingeführt. Damit kann der Service Worker Aufgaben aufschieben, bis der Nutzer wieder eine Verbindung hat. Das bedeutet, dass der Nutzer eine Nachricht eingeben und auf „Senden“ klicken kann. Dann kann er die Website verlassen und weiß, dass die Nachricht entweder jetzt gesendet wird oder sobald eine Verbindung besteht.

Dies ist ein nützliches Feature, setzt jedoch voraus, dass der Service Worker für die Dauer des Abrufs aktiv ist. Für kurze Arbeitsschritte wie das Senden einer Nachricht ist dies kein Problem. Wenn die Aufgabe jedoch zu lange dauert, beendet der Browser den Service Worker. Andernfalls besteht ein Risiko für den Datenschutz und den Akkuverbrauch des Nutzers.

Was ist also, wenn Sie etwas herunterladen möchten, das viel Zeit in Anspruch nimmt, wie einen Film, Podcasts oder bestimmte Level eines Spiels? Dazu dient Hintergrundabruf.

Seit Chrome 74 ist die Funktion „Hintergrundabruf“ standardmäßig verfügbar.

Hier ist eine kurze zweiminütige Demo, die den herkömmlichen Zustand der Vorgänge im Vergleich zur Verwendung von Hintergrundabruf zeigt:

Probiere die Demo selbst aus und sieh dir den Code an.

Funktionsweise

Ein Hintergrundabruf funktioniert so:

  1. Sie weisen den Browser an, im Hintergrund eine Gruppe von Abrufen auszuführen.
  2. Der Browser ruft diese Elemente ab und zeigt dem Nutzer den Fortschritt an.
  3. Sobald der Abruf abgeschlossen ist oder fehlgeschlagen ist, öffnet der Browser den Service Worker und löst ein Ereignis aus, um Sie darüber zu informieren, was passiert ist. Hier entscheiden Sie gegebenenfalls, was mit den Antworten geschehen soll.

Wenn der Nutzer die Seiten Ihrer Website nach Schritt 1 schließt, ist das in Ordnung und der Download wird fortgesetzt. Da der Abruf gut sichtbar ist und einfach abgebrochen werden kann, ergibt sich aus Datenschutzgründen keine viel zu lange Hintergrundsynchronisierungsaufgabe. Da der Service Worker nicht ständig ausgeführt wird, besteht keine Gefahr, dass er das System missbrauchen könnte, z. B. wenn er im Hintergrund Bitcoin abbaut.

Auf einigen Plattformen (wie Android) kann der Browser nach Schritt 1 geschlossen werden, da der Browser den Abruf an das Betriebssystem übergeben kann.

Wenn der Nutzer den Download offline startet oder während des Downloads offline geht, wird der Hintergrundabruf pausiert und später fortgesetzt.

Mit der API

Featureerkennung

Wie bei jeder neuen Funktion möchten Sie herausfinden, ob der Browser diese Funktion unterstützt. Für Background Fetch ist das so einfach:

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

Hintergrundabruf starten

Die Haupt-API hängt von einer Service Worker-Registrierung ab. Achten Sie daher darauf, dass Sie zuerst einen Service Worker registriert haben. Dann:

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

Für backgroundFetch.fetch sind drei Argumente erforderlich:

Parameter
id string
Dieser Hintergrundabruf wird eindeutig identifiziert.

backgroundFetch.fetch lehnt ab, wenn die ID mit einem vorhandenen Hintergrundabruf übereinstimmt.

requests Array<Request|string>
Die abzurufenden Elemente. Strings werden als URLs behandelt und über new Request(theString) in Request-Werte umgewandelt.

Sie können Inhalte aus anderen Quellen abrufen, solange die Ressourcen dies über CORS zulassen.

Hinweis:Chrome unterstützt derzeit keine Anfragen, für die ein CORS-Preflight erforderlich wäre.

options Ein -Objekt, das Folgendes enthalten kann:
options.title string
Ein Titel, der zusammen mit dem Fortschritt im Browser angezeigt wird.
options.icons Array<IconDefinition>
Ein Array von Objekten mit „src“, „size“ und „type“.
options.downloadTotal number
Die Gesamtgröße der Antworttexte (nach dem Entpacken mit gzip).

Obwohl dies optional ist, wird dringend empfohlen, sie anzugeben. Sie wird verwendet, um dem Nutzer mitzuteilen, wie groß der Download ist, und um Informationen zum Fortschritt bereitzustellen. Wenn Sie dies nicht angeben, teilt der Browser dem Nutzer mit, dass die Größe unbekannt ist, und bricht den Download dann eher ab.

Falls der Hintergrundabruf die hier angegebene Anzahl überschreitet, wird er abgebrochen. Es ist völlig in Ordnung, wenn der Download kleiner als downloadTotal ist. Wenn Sie sich also nicht sicher sind, wie hoch der Gesamtwert des Downloads ist, sollten Sie auf Nummer sicher gehen.

backgroundFetch.fetch gibt ein Versprechen zurück, das mit BackgroundFetchRegistration aufgelöst wird. Darauf werde ich später näher eingehen. Das Promise wird abgelehnt, wenn der Nutzer Downloads deaktiviert hat oder einer der angegebenen Parameter ungültig ist.

Durch die Bereitstellung vieler Anfragen für einen einzelnen Hintergrundabruf können Sie Dinge kombinieren, die für den Nutzer logisch einzigartig sind. Ein Film kann beispielsweise in Hunderte von Ressourcen aufgeteilt werden (üblich für MPEG-DASH) und zusätzliche Ressourcen wie Bilder enthalten. Ein Level eines Spiels kann über viele JavaScript-, Bild- und Audioressourcen verteilt sein. Für die Nutzenden ist es jedoch nur „der Film“ oder „das Level“.

Vorhandenen Hintergrundabruf abrufen

Sie können einen vorhandenen Hintergrundabruf wie folgt abrufen:

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

Dabei wird die id des gewünschten Hintergrundabrufs übergeben. get gibt undefined zurück, wenn kein aktiver Hintergrundabruf mit dieser ID vorhanden ist.

Ein Hintergrundabruf gilt ab der Registrierung als "aktiv", bis er entweder erfolgreich ist, fehlschlägt oder abgebrochen wird.

Mit getIds können Sie eine Liste aller aktiven Hintergrundabrufe abrufen:

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

Registrierungen für Hintergrundabrufe

Ein BackgroundFetchRegistration (bgFetch in den Beispielen oben) hat Folgendes:

Attribute
id string
ID des Hintergrundabrufs
uploadTotal number
Die Anzahl der Byte, die an den Server gesendet werden sollen.
uploaded number
Die Anzahl der erfolgreich gesendeten Byte.
downloadTotal number
Der Wert, der angegeben wurde, als der Hintergrundabruf registriert wurde, oder null.
downloaded number
Die Anzahl der erfolgreich empfangenen Byte.

Dieser Wert kann sich verringern. Wenn beispielsweise die Verbindung unterbrochen wird und der Download nicht fortgesetzt werden kann, startet der Browser den Abruf für diese Ressource von Grund auf neu.

result

Eines der folgenden Betriebssysteme:

  • "": Der Hintergrundabruf ist aktiv, daher gibt es noch kein Ergebnis.
  • "success": Der Hintergrundabruf war erfolgreich.
  • "failure": Der Hintergrundabruf ist fehlgeschlagen. Dieser Wert wird nur angezeigt, wenn der Hintergrundabruf vollständig fehlschlägt, da der Browser den Vorgang nicht wiederholen bzw. fortsetzen kann.
failureReason

Eines der folgenden Betriebssysteme:

  • "": Der Hintergrundabruf ist nicht fehlgeschlagen.
  • "aborted": Der Hintergrundabruf wurde vom Nutzer abgebrochen oder abort() wurde aufgerufen.
  • "bad-status": Eine der Antworten hatte den Status „Nicht okay“, z.B. 404.
  • "fetch-error": Einer der Abrufe ist aus einem anderen Grund fehlgeschlagen, z.B. wegen CORS, MIX, einer ungültigen Teilantwort oder einem allgemeinen Netzwerkfehler für einen Abruf, der nicht wiederholt werden kann.
  • "quota-exceeded": Das Speicherkontingent wurde während des Hintergrundabrufs erreicht.
  • "download-total-exceeded": Der angegebene „downloadTotal“-Wert wurde überschritten.
recordsAvailable boolean
Kann ich auf die zugrunde liegenden Anfragen/Antworten zugreifen?

Ist dieser Wert falsch, kann match und matchAll nicht mehr verwendet werden.

Methoden
abort() Gibt Folgendes zurück: Promise<boolean>
Hintergrundabruf abbrechen.

Das zurückgegebene Versprechen wird mit „true“ aufgelöst, wenn der Abruf erfolgreich abgebrochen wurde.

matchAll(request, opts) Gibt Promise<Array<BackgroundFetchRecord>> zurück.
Ruft die Anfragen und Antworten ab.

Die Argumente hier sind mit denen der Cache API identisch. Ein Aufruf ohne Argumente gibt ein Promise für alle Datensätze zurück.

Weitere Details finden Sie unten.

match(request, opts) Gibt Promise<BackgroundFetchRecord>
Wie oben zurück, wird aber mit der ersten Übereinstimmung aufgelöst.
Veranstaltungen
progress Wird ausgelöst, wenn sich uploaded, downloaded, result oder failureReason ändern.

Fortschritt wird erfasst

Dies kann über das Ereignis progress erfolgen. Denken Sie daran, dass downloadTotal der von Ihnen angegebene Wert ist oder 0, wenn Sie keinen Wert angegeben haben.

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}%`);
});

Anfragen und Antworten abrufen

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 ist ein BackgroundFetchRecord und sieht so aus:

Attribute
request Request
Die Anfrage, die gestellt wurde.
responseReady Promise<Response>
Die abgerufene Antwort.

Die Antwort liegt hinter einem Versprechen, weil sie möglicherweise noch nicht erhalten wurde. Das Promise wird abgelehnt, wenn der Abruf fehlschlägt.

Service Worker-Ereignisse

Veranstaltungen
backgroundfetchsuccess Alles wurde abgerufen.
backgroundfetchfailure Mindestens ein Abruf ist fehlgeschlagen.
backgroundfetchabort Mindestens ein Abruf ist fehlgeschlagen.

Dies ist nur dann wirklich nützlich, wenn Sie verwandte Daten bereinigen möchten.

backgroundfetchclick Der Nutzer hat auf die Benutzeroberfläche für den Downloadfortschritt geklickt.

Die Ereignisobjekte haben Folgendes:

Attribute
registration BackgroundFetchRegistration
Methoden
updateUI({ title, icons }) Hier können Sie den Titel und die Symbole ändern, die Sie anfangs festgelegt haben. Dies ist optional, ermöglicht aber bei Bedarf mehr Kontext. Dies ist nur *einmal* während der Ereignisse backgroundfetchsuccess und backgroundfetchfailure möglich.

Auf Erfolg/Missfolge reagieren

Wir haben das Ereignis progress bereits gesehen, aber es ist nur sinnvoll, wenn der Nutzer eine Seite für deine Website geöffnet hat. Der Hauptvorteil des Hintergrundabrufs besteht darin, dass die Seite weiterhin funktioniert, nachdem der Nutzer die Seite verlassen oder sogar den Browser geschlossen hat.

Wenn der Hintergrundabruf erfolgreich abgeschlossen wurde, empfängt Ihr Service Worker das Ereignis backgroundfetchsuccess und event.registration ist die Registrierung für den Hintergrundabruf.

Nach diesem Ereignis ist der Zugriff auf die abgerufenen Anfragen und Antworten nicht mehr möglich. Wenn Sie sie also behalten möchten, verschieben Sie sie an einen anderen Ort wie die Cache API.

Verwenden Sie wie bei den meisten Service Worker-Ereignissen event.waitUntil, damit der Service Worker weiß, wann das Ereignis abgeschlossen ist.

Zum Beispiel in Ihrem 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!' });
  }());
});

Der Fehler kann auf einen einzelnen 404-Fehler zurückzuführen sein, was für Sie möglicherweise nicht wichtig war. Daher kann es sich lohnen, einige Antworten wie oben in einen Cache zu kopieren.

Auf Klicks reagieren

Auf der Benutzeroberfläche werden der Downloadfortschritt und das Ergebnis angezeigt. Mit dem Ereignis backgroundfetchclick im Service Worker können Sie darauf reagieren. Wie oben ist event.registration die Registrierung für den Hintergrundabruf.

Bei diesem Ereignis müssen Sie häufig ein Fenster öffnen:

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

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

Weitere Ressourcen

Korrektur: In einer früheren Version dieses Artikels wurde Hintergrundabruf fälschlicherweise als „Webstandard“ bezeichnet. Die API befindet sich derzeit nicht im Standard-Track. Die Spezifikation finden Sie in der WICG als Entwurf für einen Community Group-Bericht.