Jetzt neu: Hintergrundabruf

Jake Archibald
Jake Archibald

2015 haben wir die Hintergrundsynchronisierung eingeführt, mit der der Service Worker Aufgaben aufschieben kann, bis der Nutzer eine Verbindung hat. Das bedeutet, dass der Nutzer eine Nachricht eingeben, auf „Senden“ klicken und die Website verlassen kann, da die Nachricht entweder sofort oder bei bestehender Verbindung gesendet wird.

Das ist eine nützliche Funktion, aber der Service Worker muss für die Dauer des Abrufs aktiv sein. Bei kurzen Aufgaben wie dem Senden einer Nachricht ist das kein Problem. Wenn die Aufgabe jedoch zu lange dauert, beendet der Browser den Service Worker, da dies sonst ein Risiko für den Datenschutz und den Akku des Nutzers darstellt.

Was ist aber, wenn Sie etwas herunterladen müssen, das lange dauern kann, z. B. einen Film, Podcasts oder Level eines Spiels? Dafür gibt es den Hintergrundabruf.

Background Fetch ist seit Chrome 74 standardmäßig verfügbar.

In dieser kurzen zweiminütigen Demo sehen Sie, wie es bisher war und wie es mit Background Fetch ist:

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 darüber zu informieren, was passiert ist. Hier entscheiden Sie, 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 gut sichtbar und leicht abzubrechen ist, besteht nicht das Datenschutzproblem einer viel zu langen Hintergrundsynchronisierungsaufgabe. Da der Service Worker nicht ständig ausgeführt wird, besteht nicht die Gefahr, dass er das System missbraucht, z. B. durch das Mining von Bitcoins im Hintergrund.

Auf einigen Plattformen (z. B. Android) kann es sein, dass der Browser nach Schritt 1 geschlossen wird, da er das Abrufen 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

Funktionserkennung

Wie bei jeder neuen Funktion müssen Sie prüfen, ob der Browser sie unterstützt. Für den Hintergrundabruf ist es ganz einfach:

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

Hintergrundabruf starten

Die Haupt-API hängt von einer Service Worker-Registrierung ab. Registrieren Sie also zuerst einen Service Worker. 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,
  });
});
.

backgroundFetch.fetch verwendet drei Argumente:

Parameter
id string
kennzeichnet diesen Hintergrundabruf eindeutig.

backgroundFetch.fetch wird abgelehnt, 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 aus 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 wäre.

options Ein Objekt, das Folgendes enthalten kann:
options.title string
Ein Titel, der im Browser zusammen mit dem Fortschritt 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 Entzippen).

Diese Angabe ist zwar optional, wird aber dringend empfohlen. Sie wird verwendet, um dem Nutzer die Größe des Downloads mitzuteilen und Fortschrittsinformationen bereitzustellen. Wenn Sie diese Angabe nicht machen, wird dem Nutzer im Browser angezeigt, dass die Größe unbekannt ist. Das kann dazu führen, dass der Nutzer den Download eher abbricht.

Wenn die Anzahl der Downloads im Hintergrund die hier angegebene Zahl überschreitet, wird der Vorgang abgebrochen. Es ist in Ordnung, wenn der Download kleiner als downloadTotal ist. Wenn Sie sich nicht sicher sind, wie groß der Download insgesamt sein wird, sollten Sie lieber etwas mehr Speicherplatz einplanen.

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

Wenn Sie viele Anfragen für einen einzelnen Hintergrundabruf bereitstellen, können Sie Dinge kombinieren, die für den Nutzer logisch zusammengehören. Ein Film kann beispielsweise in Tausende von Ressourcen aufgeteilt sein (typisch bei MPEG-DASH) und zusätzliche Ressourcen wie Bilder enthalten. Ein Level eines Spiels kann sich über viele JavaScript-, Bild- und Audioressourcen erstrecken. Für den Nutzer ist es aber einfach „der Film“ oder „das Level“.

Vorhandenen Hintergrundabruf abrufen

So können Sie einen vorhandenen Hintergrundabruf abrufen:

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

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

Ein Hintergrundabruf gilt als „aktiv“, sobald er registriert wird, 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 den Hintergrundabruf

Ein BackgroundFetchRegistration (bgFetch in den obigen Beispielen) 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 Byte.
downloadTotal number
Der Wert, der bei der Registrierung des Hintergrundabrufs angegeben 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 für diese Ressource von vorn.

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, z. B. wenn der Browser den Vorgang nicht wiederholen oder 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 einen Status, der nicht „OK“ ist, 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 während des Hintergrundabrufs erreicht.
  • "download-total-exceeded": Der angegebene Wert für `downloadTotal` wurde überschritten.
recordsAvailable boolean
Kann auf die zugrunde liegenden Anfragen/Antworten zugegriffen werden?

Wenn dieser Wert „false“ ist, können match und matchAll nicht verwendet werden.

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

Das zurückgegebene Promise 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 sind dieselben wie bei der Cache API. Wenn Sie die Funktion ohne Argumente aufrufen, wird ein Promise für alle Datensätze zurückgegeben.

Weitere Details finden Sie unten.

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

Fortschritt verfolgen

Das ist über das Ereignis progress möglich. downloadTotal ist der von Ihnen angegebene Wert 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 bereitgestellte Anfrage.
responseReady Promise<Response>
Die abgerufene Antwort.

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

Service Worker-Ereignisse

Ereignisse
backgroundfetchsuccess Alle Daten wurden erfolgreich abgerufen.
backgroundfetchfailure Mindestens einer der Abrufe ist fehlgeschlagen.
backgroundfetchabort Mindestens ein Abruf ist fehlgeschlagen.

Das ist nur wirklich nützlich, wenn Sie zugehörige 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 ursprünglich festgelegt haben. Das ist optional, aber so können Sie bei Bedarf mehr Kontext angeben. Sie können dies nur *einmal* während der Ereignisse backgroundfetchsuccess und backgroundfetchfailure tun.

Auf Erfolg/Fehler reagieren

Wir haben das Ereignis progress bereits gesehen, aber das ist nur nützlich, solange der Nutzer eine Seite Ihrer Website geöffnet hat. Der Hauptvorteil des Hintergrundabrufs besteht darin, dass die Dinge weiterhin funktionieren, nachdem der Nutzer die Seite verlassen oder den Browser sogar geschlossen hat.

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

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 Ort, z. B. in die Cache API.

Wie bei den meisten Service Worker-Ereignissen sollten Sie event.waitUntil verwenden, 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, der für Sie möglicherweise nicht wichtig war. Es kann sich also lohnen, einige Antworten wie oben in einen Cache zu kopieren.

Reaktion auf Klick

Die Benutzeroberfläche, auf der der Downloadfortschritt und das Ergebnis angezeigt werden, ist anklickbar. Mit dem backgroundfetchclick-Ereignis im Service Worker können Sie darauf reagieren. Wie oben beschrieben ist event.registration die Registrierung für das Abrufen im Hintergrund.

Die häufigste Aktion bei diesem Ereignis ist das Öffnen eines Fensters:

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 Background Fetch fälschlicherweise als „Webstandard“ bezeichnet. Die API befindet sich derzeit nicht auf dem Standard-Track. Die Spezifikation ist in WICG als Draft Community Group Report verfügbar.