Jetzt neu: Hintergrundabruf

Jake Archibald
Jake Archibald

2015 haben wir die Hintergrundsynchronisierung eingeführt, mit der der Dienst-Worker Aufgaben verschieben kann, bis der Nutzer eine Internetverbindung hat. Das bedeutet, dass der Nutzer eine Nachricht eingeben, auf „Senden“ klicken und die Website verlassen kann, in dem Wissen, dass die Nachricht entweder jetzt oder sobald er eine Verbindung hat, gesendet wird.

Das ist eine nützliche Funktion, erfordert aber, dass der Dienst-Worker während der gesamten Abrufzeit aktiv ist. Dies ist kein Problem für kurze Arbeitsschritte wie das Senden einer Nachricht. Wenn die Aufgabe jedoch zu lange dauert, beendet der Browser den Service Worker, da dies andernfalls ein Risiko für den Datenschutz und den Akku des Nutzers darstellt.

Was ist, wenn Sie etwas herunterladen möchten, was viel Zeit in Anspruch nimmt, z. B. einen Film, Podcasts oder Level eines Spiels? Dafür gibt es den Hintergrundabruf.

Der Hintergrundabruf ist seit Chrome 74 standardmäßig verfügbar.

In dieser zweiminütigen Demo wird der herkömmliche Zustand verglichen mit der Verwendung von Hintergrundabruf:

Probieren Sie die Demo selbst aus und sehen Sie sich den Code an.

Funktionsweise

So funktioniert ein Hintergrundabruf:

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

Wenn der Nutzer nach Schritt 1 Seiten Ihrer Website schließt, ist das in Ordnung. Der Download wird fortgesetzt. Da der Abruf sehr sichtbar ist und einfach abgebrochen werden kann, gibt es keine Datenschutzbedenken einer zu langwierigen Hintergrundsynchronisierung. Da der Dienst-Worker nicht ständig ausgeführt wird, besteht keine Gefahr, dass er das System missbraucht, z. B. durch Bitcoin-Mining im Hintergrund.

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

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

Mit der API

Funktion erkennen

Wie bei jeder neuen Funktion möchten Sie herausfinden, ob der Browser dies unterstützt. So aktivieren Sie den Hintergrundabruf:

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, zuerst einen Service Worker zu registrieren. 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
Identifiziert diesen Hintergrundabruf eindeutig.

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 Requests umgewandelt.

Sie können Inhalte von anderen Ursprüngen abrufen, sofern die Ressourcen dies über CORS zulassen.

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

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 Antwortkörper (nach dem Entpacken).

Diese Angabe ist optional, wird aber dringend empfohlen. Sie gibt dem Nutzer Aufschluss über die Größe des Downloads und den Fortschritt. Wenn Sie diese Angabe nicht machen, wird dem Nutzer im Browser angezeigt, dass die Größe unbekannt ist. Dies kann dazu führen, dass der Nutzer den Download abbricht.

Wenn die Anzahl der Downloads im Hintergrund die hier angegebene Zahl überschreitet, wird der Vorgang abgebrochen. Es ist völlig in Ordnung, wenn der Download kleiner als die downloadTotal ist. Wenn du dir nicht sicher bist, wie groß der Download insgesamt sein wird, solltest du lieber auf Nummer sicher gehen.

backgroundFetch.fetch gibt ein Versprechen zurück, das mit einer BackgroundFetchRegistration aufgelöst wird. Die Details dazu erläutere ich später. Das Versprechen wird abgelehnt, wenn der Nutzer Downloads deaktiviert hat oder einer der angegebenen Parameter ungültig ist.

Wenn Sie mehrere Anfragen für einen einzelnen Hintergrundabruf bereitstellen, können Sie Dinge kombinieren, die für den Nutzer logisch nur eine Sache sind. Ein Film kann beispielsweise in Tausende von Ressourcen aufgeteilt sein (typisch für MPEG-DASH) und zusätzliche Ressourcen wie Bilder enthalten. Ein Level eines Spiels kann auf viele JavaScript-, Bild- und Audioressourcen verteilt werden. Für den Nutzer ist es jedoch nur „der Film“ oder „das Level“.

Vorhandenen Hintergrundabruf abrufen

So rufen Sie einen vorhandenen Hintergrundabruf ab:

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

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

Ein Hintergrundabruf gilt ab dem Zeitpunkt seiner Registrierung als „aktiv“, bis er entweder erfolgreich abgeschlossen, fehlgeschlagen oder abgebrochen wurde.

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 den Hintergrundabruf

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

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

Dieser Wert kann sinken. Wenn beispielsweise die Verbindung unterbrochen wird und der Download nicht fortgesetzt werden kann, startet der Browser den Abruf dieser Ressource von vorn.

result

Eines der folgenden Betriebssysteme:

  • "": Der Abruf im Hintergrund ist aktiv, es gibt also 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 nicht noch einmal versuchen oder fortsetzen kann.
failureReason

Eines der folgenden Betriebssysteme:

  • "": Der Abruf im Hintergrund ist nicht fehlgeschlagen.
  • "aborted": Der Abruf im Hintergrund wurde vom Nutzer abgebrochen oder abort() wurde aufgerufen.
  • "bad-status" – Eine der Antworten hatte einen nicht ordnungsgemäßen Status, z.B. 404.
  • "fetch-error": Einer der Abrufe ist aus einem anderen Grund fehlgeschlagen, z.B. CORS, MIX, eine ungültige Teilantwort oder ein allgemeiner Netzwerkfehler bei einem Abruf, der nicht wiederholt werden kann.
  • "quota-exceeded" – Das Speicherkontingent wurde beim Abrufen im Hintergrund erreicht.
  • "download-total-exceeded": Der angegebene Wert für „downloadTotal“ wurde überschritten.
recordsAvailable boolean
Können die zugrunde liegenden Anfragen/Antworten abgerufen werden?

Sobald dies der Fall ist, können match und matchAll nicht mehr verwendet werden.

Methoden
abort() Gibt Promise<boolean>
zurück, um den Hintergrundabruf abzubrechen.

Das zurückgegebene Versprechen wird mit „wahr“ abgeschlossen, wenn der Abruf erfolgreich abgebrochen wurde.

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

Die Argumente sind hier dieselben wie bei der Cache API. Wenn Sie die Funktion ohne Argumente aufrufen, wird ein Versprechen für alle Datensätze zurückgegeben.

Weitere Details finden Sie unten.

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

Fortschritt wird erfasst

Das ist über das Ereignis progress möglich. 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 eine BackgroundFetchRecord und sieht so aus:

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

Die Antwort ist hinter einem Versprechen, weil sie möglicherweise noch nicht eingegangen ist. Das Versprechen wird abgelehnt, wenn der Abruf fehlschlägt.

Service Worker-Ereignisse

Ereignisse
backgroundfetchsuccess Alle Daten wurden erfolgreich abgerufen.
backgroundfetchfailure Mindestens eine Abfrage ist fehlgeschlagen.
backgroundfetchabort Mindestens ein Abruf ist fehlgeschlagen.

Das ist nur dann wirklich sinnvoll, wenn Sie ähnliche Daten bereinigen möchten.

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

Die Ereignisobjekte haben folgende Eigenschaften:

Attribute
registration BackgroundFetchRegistration
Methoden
updateUI({ title, icons }) Hier können Sie den ursprünglich festgelegten Titel und die Symbole ändern. Dies ist optional, aber Sie können bei Bedarf mehr Kontext angeben. Bei backgroundfetchsuccess- und backgroundfetchfailure-Ereignissen ist dies nur *einmal* möglich.

Auf Erfolg oder Misserfolg reagieren

Wir haben das Ereignis progress bereits kennengelernt. Es ist jedoch nur dann nützlich, wenn der Nutzer eine Seite Ihrer Website geöffnet hat. Der Hauptvorteil des Hintergrundabrufs besteht darin, dass die Inhalte auch dann funktionieren, wenn der Nutzer die Seite verlässt oder sogar den Browser schließt.

Wenn der Abruf im Hintergrund erfolgreich abgeschlossen wurde, erhält Ihr Service Worker das Ereignis backgroundfetchsuccess. event.registration ist die Registrierung für den Abruf im Hintergrund.

Nach diesem Ereignis sind die abgerufenen Anfragen und Antworten nicht mehr zugänglich. Wenn Sie sie behalten möchten, verschieben Sie sie an einen anderen Speicherort, z. B. in die Cache API.

Wie bei den meisten Dienstworker-Ereignissen sollten Sie event.waitUntil verwenden, damit der Dienstworker weiß, wann das Ereignis abgeschlossen ist.

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

Möglicherweise ist der Fehler auf eine einzelne 404-Antwort zurückzuführen, die für Sie nicht wichtig war. Es kann sich daher trotzdem lohnen, einige Antworten wie oben beschrieben in einen Cache zu kopieren.

Auf Klicks reagieren

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

Normalerweise wird bei diesem Ereignis ein Fenster geöffnet:

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

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

Zusätzliche Ressourcen

Korrektur: In einer früheren Version dieses Artikels wurde fälschlicherweise behauptet, dass der Abruf im Hintergrund ein „Webstandard“ ist. Die API befindet sich derzeit nicht in der Entwicklungsphase. Die Spezifikation finden Sie im WICG als Entwurf für einen Community-Gruppenbericht.