Introduzione al recupero in background

Jake Archibald
Jake Archibald

Nel 2015 abbiamo introdotto la sincronizzazione in background, che consente al service worker di rinviare il lavoro finché l'utente non dispone della connettività. Ciò significa che l'utente può digitare messaggio, premere Invia ed uscire dal sito sapendo che il messaggio verrà inviato ora o quando la connettività.

Si tratta di una funzionalità utile, ma richiede che il service worker resti attivo per l'intera durata del recupero. Questo non è un problema per brevi operazioni come l'invio di un messaggio, ma se l'attività richiede troppo a lungo il browser termina il service worker, altrimenti si comporterebbe un rischio per la privacy dell'utente e batteria.

E se devi scaricare qualcosa che potrebbe richiedere molto tempo, come un film, dei podcast o livelli di un gioco. Ecco a cosa serve il recupero in background.

Il recupero in background è disponibile per impostazione predefinita a partire da Chrome 74.

Ecco una rapida demo di due minuti che mostra il tradizionale stato delle cose a confronto con il recupero in background:

Prova la demo e sfoglia il codice.

Come funziona

Il recupero dello sfondo funziona in questo modo:

  1. Chiedi al browser di eseguire un gruppo di recuperi in background.
  2. Il browser recupera questi elementi, mostrando i progressi all'utente.
  3. Una volta completato o non riuscito il recupero, il browser apre il service worker e attiva un evento. di dirti cosa è successo. È qui che decidi cosa fare con le risposte, se necessario.

Se l'utente chiude le pagine del sito dopo il passaggio 1, non c'è problema e il download continuerà. Poiché il recupero è molto visibile e può essere interrotto, non esiste un problema di privacy troppo lungo dell'attività di sincronizzazione in background. Poiché il service worker non è costantemente in esecuzione, non c'è alcun problema che potrebbe abusare del sistema, ad esempio eseguendo il mining di bitcoin in background.

Su alcune piattaforme (come Android) è possibile che il browser si chiuda dopo il passaggio 1, ad esempio un browser può trasferire il recupero al sistema operativo.

Se l'utente avvia il download offline o passa alla modalità offline durante il download, lo sfondo il recupero verrà messo in pausa e ripreso in un secondo momento.

L'API

Rilevamento delle caratteristiche

Come per ogni nuova funzionalità, vuoi verificare se il browser la supporta. Per il recupero in background, semplice come:

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

Avvio di un recupero in background

L'API principale si blocca, la registrazione di un service worker quindi assicurati di aver registrato un service worker. Quindi:

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 accetta tre argomenti:

Parametri
id string
identifica in modo univoco questo recupero in background.

backgroundFetch.fetch rifiuterà se l'ID corrisponde a uno sfondo esistente recupero.

requests Array<Request|string>
Gli elementi da recuperare. Le stringhe verranno trattate come URL e trasformate in Request tramite new Request(theString).

Puoi recuperare elementi da altre origini, purché le risorse lo permettano tramite CORS.

Nota: al momento Chrome non supporta le richieste che potrebbero richiedono un preflight CORS.

options Un oggetto che può includere quanto segue:
options.title string
Un titolo da visualizzare nel browser insieme all'avanzamento.
options.icons Array<IconDefinition>
Un array di oggetti con gli attributi "src", "size" e "type".
options.downloadTotal number
La dimensione totale dei corpi della risposta (dopo essere stati decompressi).

Anche se si tratta di un valore facoltativo, ti consigliamo vivamente di fornirlo. È utilizzato per raccontare all'utente le dimensioni del download e per fornire informazioni sull'avanzamento. Se non fornisci il browser comunicherà all'utente che le dimensioni sono sconosciute e, di conseguenza, l'utente potrebbe essere più interromperanno il download.

Se i download del recupero in background superano il numero specificato qui, l'operazione verrà interrotta. È perfettamente se il download è inferiore a downloadTotal, quindi se non disponi con certezza quale sarà il totale dei download, è meglio procedere con cautela.

backgroundFetch.fetch restituisce una promessa che si risolve con un BackgroundFetchRegistration. Io ne parleremo in dettaglio più avanti. La promessa viene rifiutata se l'utente ha disattivato i download o se dei parametri forniti non è valido.

Se fornisci molte richieste per un singolo recupero in background, puoi combinare elementi che sono logicamente una cosa sola per l'utente. Ad esempio, un film può essere suddiviso in migliaia di risorse (tipicamente con MPEG-DASH, e includono risorse aggiuntive come le immagini. Un livello di un gioco può essere distribuito le risorse JavaScript, immagine e audio. Per l'utente si tratta solo di "il film" o "il livello".

Recupero di un recupero dello sfondo esistente

Puoi ottenere un recupero dello sfondo esistente come questo:

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

...trasmettendo l'id del recupero dello sfondo che vuoi. get restituisce undefined se non sono presenti recupero attivo dello sfondo con quell'ID.

Un recupero in background è considerato "attivo" dal momento della registrazione fino al completamento dell'operazione, non funziona o viene interrotta.

Puoi ottenere un elenco di tutti i recuperi attivi dello sfondo utilizzando getIds:

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

Registrazioni di recupero in background

Un BackgroundFetchRegistration (bgFetch negli esempi precedenti) ha quanto segue:

Proprietà
id string
ID del recupero dello sfondo.
uploadTotal number
Il numero di byte da inviare al server.
uploaded number
Il numero di byte inviati correttamente.
downloadTotal number
Il valore fornito al momento della registrazione del recupero dello sfondo oppure zero.
downloaded number
Il numero di byte ricevuti.

Questo valore potrebbe diminuire. Ad esempio, se la connessione si interrompe e il download non può essere in questo caso il browser riavvia il recupero per quella risorsa da zero.

result

Il valore sarà uno dei seguenti:

  • "": il recupero dello sfondo è attivo, quindi non è ancora disponibile alcun risultato.
  • "success": il recupero dello sfondo è riuscito.
  • "failure": il recupero dello sfondo non è riuscito. Questo valore viene visualizzato solo il recupero in background non riesce completamente perché il browser non può riprovare/riprendere.
failureReason

Il valore sarà uno dei seguenti:

  • "": il recupero dello sfondo non è riuscito.
  • "aborted" - Il recupero dello sfondo è stato interrotto dall'utente oppure Hai chiamato abort().
  • "bad-status": una delle risposte aveva uno stato Non OK, ad esempio 404.
  • "fetch-error": uno dei recuperi non è riuscito per altri motivi, ad esempio CORS, MIX, una risposta parziale non valida o un errore di rete generico per un recupero che e non è possibile ritentare.
  • "quota-exceeded" - È stata raggiunta la quota di spazio di archiviazione in background recupero.
  • "download-total-exceeded" - Il valore "downloadTotal" fornito era superato.
recordsAvailable boolean
È possibile accedere alle richieste/risposte sottostanti?

Se questo valore è falso, non è possibile utilizzare match e matchAll.

Metodi
abort() Restituisce Promise<boolean>
Interrompi il recupero dello sfondo.

La promessa restituita si risolve con true se il recupero è stato interrotto correttamente.

matchAll(request, opts) Resi Promise<Array<BackgroundFetchRecord>>
Recupera le richieste e risposte.

Gli argomenti qui sono gli stessi di nella cache tramite Google Cloud. Una chiamata senza argomenti restituisce una promessa per tutti i record.

Per ulteriori dettagli, continua a leggere.

match(request, opts) Restituisce Promise<BackgroundFetchRecord>
Come sopra, ma risolve con la prima corrispondenza.
Eventi
progress Attivato quando uno dei seguenti valori: uploaded, downloaded, result o failureReason modifica.

Monitoraggio dei progressi

Questa operazione può essere eseguita tramite l'evento progress. Ricorda che downloadTotal è qualsiasi valore tu fornito o 0 se non hai fornito un valore.

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

Recuperare le richieste e le risposte

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 è un BackgroundFetchRecord e ha il seguente aspetto:

Proprietà
request Request
La richiesta fornita.
responseReady Promise<Response>
La risposta recuperata.

La risposta è dietro una promessa perché potrebbe non essere ancora stata ricevuta. La promessa rifiuterà se il recupero non va a buon fine.

Eventi dei service worker

Eventi
backgroundfetchsuccess Tutti gli elementi sono stati recuperati correttamente.
backgroundfetchfailure Uno o più recuperi non sono riusciti.
backgroundfetchabort Uno o più recuperi non sono riusciti.

Questa operazione è molto utile solo se vuoi eseguire la pulizia dei dati correlati.

backgroundfetchclick L'utente ha fatto clic sull'interfaccia utente di avanzamento del download.

Gli oggetti evento hanno quanto segue:

Proprietà
registration BackgroundFetchRegistration
Metodi
updateUI({ title, icons }) Consente di modificare il titolo/le icone inizialmente impostate. È facoltativo, ma ti consente fornisci maggiore contesto, se necessario. Puoi farlo solo *una* volta durante Eventi backgroundfetchsuccess e backgroundfetchfailure.

Reagire a successi/insuccessi

Abbiamo già visto l'evento progress, ma è utile solo quando l'utente ha una pagina aperta per nel tuo sito. Il vantaggio principale del recupero in background è che le cose continuano a funzionare anche dopo che l'utente lascia la pagina o persino chiude il browser.

Se il recupero in background viene completato correttamente, il service worker riceverà backgroundfetchsuccess evento e event.registration sarà la registrazione del recupero in background.

Dopo questo evento, le richieste e le risposte recuperate non sono più accessibili, quindi se vuoi e conservarli, spostali in una posizione come l'API cache.

Come per la maggior parte degli eventi dei service worker, utilizza event.waitUntil in modo che il service worker sappia quando si verifica l'evento l'operazione.

Ad esempio, nel tuo 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'errore può essere dovuto a un singolo errore 404, il che potrebbe non essere stato importante per te, quindi vale comunque la pena copiare alcune risposte in una cache come sopra.

Reazione al clic

L'UI che mostra l'avanzamento e il risultato del download è cliccabile. Evento backgroundfetchclick a il service worker consente di reagire a questo. Come sopra event.registration sarà lo sfondo la registrazione del recupero.

In genere questo evento viene aperto da una finestra:

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

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

Risorse aggiuntive

Correzione: una versione precedente di questo articolo ha erroneamente definito il recupero in background come uno "standard web". Al momento l'API non è in linea con gli standard, la specifica è disponibile in WICG come bozza di report del gruppo della community.