Introductie van achtergrond ophalen

Jake Archibald
Jake Archibald

In 2015 introduceerden we Achtergrondsynchronisatie , waarmee de servicemedewerker werk kan uitstellen totdat de gebruiker weer verbinding heeft. Dit betekent dat de gebruiker een bericht kan typen, op verzenden kan klikken en de site kan verlaten, wetende dat het bericht nu wordt verzonden of zodra de gebruiker weer verbinding heeft.

Het is een handige functie, maar vereist wel dat de service worker actief blijft gedurende de hele ophaalactie. Dat is geen probleem voor korte taken zoals het versturen van een bericht, maar als de taak te lang duurt, sluit de browser de service worker af, anders loopt de privacy en batterij van de gebruiker gevaar.

Dus, wat als je iets moet downloaden dat lang kan duren, zoals een film, podcasts of levels van een game? Daar is Background Fetch voor.

Achtergrond ophalen is standaard beschikbaar sinds Chrome 74.

Hier is een korte demo van twee minuten die de traditionele stand van zaken laat zien, vergeleken met het gebruik van Background Fetch:

Hoe het werkt

Het ophalen van de achtergrond werkt als volgt:

  1. U vertelt de browser dat deze op de achtergrond een aantal ophaalacties moet uitvoeren.
  2. De browser haalt deze gegevens op en toont de voortgang aan de gebruiker.
  3. Zodra het ophalen is voltooid of mislukt, opent de browser uw service worker en genereert een gebeurtenis om u te laten weten wat er is gebeurd. Hier bepaalt u wat er eventueel met de reacties moet gebeuren.

Als de gebruiker na stap 1 pagina's van je site sluit, is dat geen probleem; de download gaat gewoon door. Omdat de fetch goed zichtbaar is en gemakkelijk kan worden afgebroken, is er geen sprake van privacyproblemen door een te lange synchronisatietaak op de achtergrond. Omdat de service worker niet constant draait, is er geen risico dat deze het systeem zou kunnen misbruiken, zoals bitcoinmining op de achtergrond.

Op sommige platforms (zoals Android) kan het voorkomen dat de browser na stap 1 sluit, omdat de browser het ophalen aan het besturingssysteem kan overdragen.

Als de gebruiker het downloaden start terwijl hij/zij offline is, of offline gaat tijdens het downloaden, wordt het ophalen op de achtergrond gepauzeerd en later hervat.

De API

Functiedetectie

Zoals met elke nieuwe functie wilt u controleren of de browser deze ondersteunt. Voor het ophalen op de achtergrond is het zo eenvoudig als:

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

Een achtergrondophaling starten

De belangrijkste API is afhankelijk van de registratie van een service worker , dus zorg ervoor dat u eerst een service worker hebt geregistreerd. Vervolgens:

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 heeft drie argumenten:

Parameters
id string
Identificeert deze achtergrondophaling op unieke wijze.

backgroundFetch.fetch wordt afgewezen als de ID overeenkomt met een bestaande achtergrondophaling.

requests Array< Request |string>
De op te halen items. Strings worden behandeld als URL's en omgezet in Request via new Request(theString) .

Je kunt dingen ophalen van andere bronnen, zolang de bronnen dit toelaten via CORS .

Let op: Chrome ondersteunt momenteel geen verzoeken waarvoor een CORS-preflight nodig is.

options Een object dat het volgende kan omvatten:
options.title string
Een titel die de browser samen met de voortgang weergeeft.
options.icons Array< IconDefinition >
Een reeks objecten met een `src`, `size` en `type`.
options.downloadTotal number
De totale grootte van de antwoordteksten (nadat ze zijn uitgepakt).

Hoewel dit optioneel is, wordt het sterk aanbevolen om het wel te vermelden. Het wordt gebruikt om de gebruiker te informeren over de grootte van de download en om informatie over de voortgang te verstrekken. Als u dit niet doet, zal de browser de gebruiker laten weten dat de grootte onbekend is, waardoor de kans groter is dat de gebruiker de download afbreekt.

Als het aantal downloads op de achtergrond het hier opgegeven aantal overschrijdt, wordt de download afgebroken. Het is prima als de download kleiner is dan de downloadTotal . Als u niet zeker weet wat het totale downloadbedrag zal zijn, is het dus verstandig om voorzichtig te zijn.

backgroundFetch.fetch retourneert een promise die resulteert in een BackgroundFetchRegistration . Ik zal later meer details hierover bespreken. De promise wordt afgewezen als de gebruiker zich heeft afgemeld voor downloads, of als een van de opgegeven parameters ongeldig is.

Door meerdere verzoeken voor één achtergrondophaling in te dienen, kun je dingen combineren die voor de gebruiker logischerwijs één ding zijn. Een film kan bijvoorbeeld worden opgedeeld in duizenden bronnen (typisch met MPEG-DASH ) en worden geleverd met extra bronnen zoals afbeeldingen. Een level van een game kan verspreid zijn over meerdere JavaScript-, afbeeldings- en audiobronnen. Maar voor de gebruiker is het gewoon "de film" of "het level".

Een bestaande achtergrond ophalen

U kunt een bestaande achtergrond als volgt ophalen:

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

…door de id van de gewenste achtergrondophaling door te geven. get retourneert undefined als er geen actieve achtergrondophaling is met die ID.

Een achtergrondophaling wordt als 'actief' beschouwd vanaf het moment dat deze wordt geregistreerd, totdat deze slaagt, mislukt of wordt afgebroken.

Met getIds kunt u een lijst met alle actieve achtergrondophalingen verkrijgen:

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

Achtergrondophaalregistraties

Een BackgroundFetchRegistration ( bgFetch in de bovenstaande voorbeelden) heeft het volgende:

Eigenschappen
id string
De ID van de achtergrondophaling.
uploadTotal number
Het aantal bytes dat naar de server moet worden verzonden.
uploaded number
Het aantal bytes dat succesvol is verzonden.
downloadTotal number
De waarde die is opgegeven bij het registreren van het ophalen van de achtergrond, of nul.
downloaded number
Het aantal bytes dat succesvol is ontvangen.

Deze waarde kan dalen. Bijvoorbeeld, als de verbinding wegvalt en de download niet kan worden hervat, start de browser het ophalen van die bron opnieuw.

result

Een van de volgende:

  • "" - Het ophalen op de achtergrond is actief, dus er is nog geen resultaat.
  • "success" - Het ophalen van de achtergrond was succesvol.
  • "failure" - Het ophalen op de achtergrond is mislukt. Deze waarde wordt alleen weergegeven wanneer het ophalen op de achtergrond volledig mislukt, bijvoorbeeld wanneer de browser niet opnieuw kan proberen/hervatten.
failureReason

Een van de volgende:

  • "" - Het ophalen van de achtergrond is niet mislukt.
  • "aborted" – Het ophalen van de achtergrond is door de gebruiker afgebroken, of abort() is aangeroepen.
  • "bad-status" - Een van de reacties had de status 'niet ok', bijvoorbeeld 404.
  • "fetch-error" - Een van de ophaalbewerkingen is om een andere reden mislukt, bijvoorbeeld CORS, MIX, een ongeldige gedeeltelijke respons of een algemene netwerkfout voor een ophaalbewerking die niet opnieuw kan worden geprobeerd.
  • "quota-exceeded" - Opslagquotum is bereikt tijdens het ophalen op de achtergrond.
  • "download-total-exceeded" - De opgegeven `downloadTotal` is overschreden.
recordsAvailable boolean
Zijn de onderliggende verzoeken/reacties toegankelijk?

Als dit onwaar is, kunnen match en matchAll niet worden gebruikt.

Methoden
abort() Retourneert Promise<boolean>
Het ophalen van de achtergrond annuleren.

De geretourneerde belofte wordt opgelost met true als het ophalen succesvol is afgebroken.

matchAll(request, opts) Retourneert Promise<Array<BackgroundFetchRecord>>
Ontvang de verzoeken en reacties.

De argumenten hier zijn hetzelfde als die van de cache-API . Aanroepen zonder argumenten retourneert een promise voor alle records.

Zie hieronder voor meer details.

match(request, opts) Retourneert Promise<BackgroundFetchRecord>
Zoals hierboven, maar opgelost met de eerste match.
Evenementen
progress Wordt geactiveerd wanneer een van de volgende variabelen verandert: uploaded , downloaded , result failureReason .

Voortgang volgen

Dit kan via de progress . Onthoud dat downloadTotal de waarde is die u hebt opgegeven, of 0 als u geen waarde hebt opgegeven.

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

Het ontvangen van de verzoeken en reacties

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 is een BackgroundFetchRecord en ziet er zo uit:

Eigenschappen
request Request
Het verzoek dat werd gedaan.
responseReady Promise<Response>
Het opgehaalde antwoord.

Het antwoord staat achter een belofte, omdat het mogelijk nog niet is ontvangen. De belofte wordt afgewezen als het ophalen mislukt.

Evenementen voor servicemedewerkers

Evenementen
backgroundfetchsuccess Alles is succesvol opgehaald.
backgroundfetchfailure Eén of meer ophaalacties zijn mislukt.
backgroundfetchabort Eén of meer ophaalacties zijn mislukt.

Dit is alleen echt nuttig als u opschoning van gerelateerde gegevens wilt uitvoeren.

backgroundfetchclick De gebruiker heeft op de gebruikersinterface voor de downloadvoortgang geklikt.

De gebeurtenisobjecten hebben het volgende:

Eigenschappen
registration BackgroundFetchRegistration
Methoden
updateUI({ title, icons }) Hiermee kunt u de titel/pictogrammen die u oorspronkelijk hebt ingesteld, wijzigen. Dit is optioneel, maar het geeft u de mogelijkheid om indien nodig meer context te bieden. U kunt dit *slechts* doen tijdens de gebeurtenissen backgroundfetchsuccess en backgroundfetchfailure .

Reageren op succes/falen

We hebben de progress al gezien, maar die is alleen nuttig zolang de gebruiker een pagina op je site open heeft staan. Het belangrijkste voordeel van ophalen op de achtergrond is dat alles blijft werken nadat de gebruiker de pagina verlaat of zelfs de browser sluit.

Als het ophalen van de achtergrond succesvol wordt voltooid, ontvangt uw service worker de gebeurtenis backgroundfetchsuccess en wordt event.registration de registratie voor het ophalen van de achtergrond.

Na deze gebeurtenis zijn de opgehaalde verzoeken en antwoorden niet meer toegankelijk. Als u ze wilt bewaren, verplaatst u ze naar een locatie, bijvoorbeeld de cache-API .

Zoals bij de meeste service worker-gebeurtenissen gebruikt u event.waitUntil , zodat de service worker weet wanneer de gebeurtenis is voltooid.

Bijvoorbeeld in uw servicemedewerker:

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

Het kan zijn dat de foutmelding slechts één 404 is, wat voor u niet van belang is. In dat geval kan het de moeite waard zijn om een aantal reacties naar een cache te kopiëren, zoals hierboven beschreven.

Reageren op klikken

De gebruikersinterface die de downloadvoortgang en het resultaat weergeeft, is klikbaar. Met de gebeurtenis backgroundfetchclick in de service worker kunt u hierop reageren. Zoals hierboven beschreven, zal event.registration de registratie op de achtergrond zijn.

Het gebruikelijke wat je bij deze gebeurtenis doet, is een venster openen:

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

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

Aanvullende bronnen

Correctie: In een eerdere versie van dit artikel werd Background Fetch ten onrechte aangeduid als een "webstandaard". De API is momenteel niet op het standaardisatiepad; de specificatie is te vinden in WICG als een concept Community Group Report.