Introduzione al recupero in background

Jake Archibald
Jake Archibald

Nel 2015 abbiamo introdotto la sincronizzazione in background, che consente al service worker di posticipare il lavoro finché l'utente non dispone di connettività. Ciò significa che l'utente può digitare un messaggio, premere Invia e lasciare il sito sapendo che il messaggio verrà inviato ora o quando sarà disponibile la connettività.

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

E se dovessi scaricare qualcosa che potrebbe richiedere molto tempo, come un film, dei podcast o i livelli di un gioco? Questo è lo scopo del 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 lo stato tradizionale delle cose rispetto all'uso del recupero in background:

Prova a utilizzare la demo e sfoglia il codice.

Come funziona

Un recupero in background funziona nel seguente modo:

  1. Puoi chiedere al browser di eseguire un gruppo di recuperi in background.
  2. Il browser recupera questi elementi, mostrando all'utente lo stato di avanzamento.
  3. Una volta che il recupero è stato completato o non è riuscito, il browser apre il service worker e attiva un evento per informarti che cosa è successo. È qui che decidi cosa fare, se pertinente, con le risposte.

Se l'utente chiude le pagine del tuo sito dopo il passaggio 1, il download continuerà. Poiché il recupero è altamente visibile e facilmente interrompebile, un'attività di sincronizzazione in background troppo lunga non sussiste alcun problema di privacy. Poiché il service worker non è sempre in esecuzione, non temere che possa utilizzare in modo illecito il sistema, ad esempio eseguendo il mining di bitcoin in background.

Su alcune piattaforme (come Android) è possibile che il browser venga chiuso dopo il passaggio 1, poiché il browser può trasferire il recupero al sistema operativo.

Se l'utente avvia il download mentre è offline o passa alla modalità offline durante il download, il recupero in background viene messo in pausa e ripreso in un secondo momento.

L'API

Rilevamento delle funzionalità

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

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

Avvio di un recupero in background

L'API principale blocca la registrazione di un service worker, perciò assicurati di aver prima 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 un recupero in background esistente.

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 a condizione che le risorse lo consentano tramite CORS.

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

options Un oggetto che può includere quanto segue:
options.title string
Un titolo che il browser mostra insieme all'avanzamento.
options.icons Array<IconDefinition>
Un array di oggetti con "src", "size" e "type".
options.downloadTotal number
La dimensione totale dei corpi delle risposte (dopo l'un-compressione con gzip).

Anche se è facoltativo, ti consigliamo vivamente di fornirlo. Viene utilizzato per comunicare all'utente le dimensioni del download e per fornire informazioni sullo stato di avanzamento. Se non fornisci questa informazione, il browser comunicherà all'utente che la dimensione è sconosciuta e, di conseguenza, è più probabile che l'utente interrompa il download.

Se i download del recupero in background superano il numero specificato, l'operazione verrà interrotta. Non è un problema se le dimensioni del download sono inferiori a downloadTotal, quindi se non sei sicuro del totale dei download, ti consigliamo di prestare attenzione.

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

Se fornisci molte richieste per un singolo recupero in background, puoi combinare elementi che logicamente sono un unico elemento per l'utente. Ad esempio, un filmato potrebbe essere suddiviso in migliaia di risorse (in genere con MPEG-DASH) e avere risorse aggiuntive come le immagini. Un livello di un gioco può essere distribuito su molte risorse JavaScript, immagine e audio. Ma per l'utente si tratta solo di "il film" o "il livello".

Recupero di un recupero in background esistente

Puoi ottenere un recupero dello sfondo esistente come segue:

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

... passando l'id del recupero dello sfondo che vuoi. get restituisce undefined se non è presente alcun recupero attivo dello sfondo con quell'ID.

Un recupero in background è considerato "attivo" dal momento in cui viene registrato, fino a quando non riesce, non riesce o viene interrotto.

Puoi ottenere un elenco di tutti i recuperi in background attivi utilizzando getIds:

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

Registrazioni 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.
downloadTotal number
Il valore fornito al momento della registrazione del recupero in background oppure pari a zero.
downloaded number
Il numero di byte ricevuti.

Questo valore potrebbe diminuire. Ad esempio, se la connessione si interrompe e non è possibile riprendere il download, il browser riavvia il recupero della risorsa da zero.

result

Il valore sarà uno dei seguenti:

  • "": il recupero dello sfondo è attivo, quindi non è disponibile ancora alcun risultato.
  • "success": il recupero dello sfondo è stato eseguito correttamente.
  • "failure": recupero in background non riuscito. Questo valore viene visualizzato solo quando il recupero in background non riesce del tutto, in quanto il browser non può riprovare/riprenderlo.
failureReason

Il valore sarà uno dei seguenti:

  • "" - Il recupero dello sfondo non è riuscito.
  • "aborted": il recupero in background è stato interrotto dall'utente oppure è stato richiamato abort().
  • "bad-status": una delle risposte aveva uno stato non corretto, 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 generale per un recupero che non è possibile riprovare.
  • "quota-exceeded": la quota di archiviazione è stata raggiunta durante il recupero in background.
  • "download-total-exceeded": il valore "downloadTotal" fornito è stato superato.
recordsAvailable boolean
È possibile accedere alle richieste/risposte sottostanti?

Una volta impostato il valore falso, match e matchAll non possono essere utilizzati.

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

La promessa restituita viene risolta con true se il recupero è stato interrotto correttamente.

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

Gli argomenti qui sono gli stessi dell'API cache. La chiamata senza argomenti restituisce una promessa per tutti i record.

Continua a leggere per saperne di più.

match(request, opts) Restituisce Promise<BackgroundFetchRecord>
Come sopra, ma risolve la prima corrispondenza.
Eventi
progress Attivato quando viene modificata una delle seguenti opzioni: uploaded, downloaded, result o failureReason.

Monitoraggio dei progressi

Questa operazione può essere eseguita tramite l'evento progress. Ricorda che downloadTotal è qualsiasi valore tu hai fornito oppure 0 se non hai fornito alcun 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}%`);
});

Recupero delle richieste e delle 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 stata ancora ricevuta. La promessa verrà rifiutata se il recupero non va a buon fine.

Eventi del service worker

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

Questa opzione è 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 }) Ti consente di modificare il titolo e le icone impostate inizialmente. Questa rappresentazione è facoltativa, ma consente di fornire maggiore contesto, se necessario. Puoi farlo *una volta* durante gli eventi backgroundfetchsuccess e backgroundfetchfailure.

Reazioni al successo o al fallimento

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

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

Dopo questo evento, le richieste e le risposte recuperate non sono più accessibili, quindi se vuoi conservarle, spostale in un percorso come l'API cache.

Come per la maggior parte degli eventi del service worker, utilizza event.waitUntil in modo che il service worker sappia quando l'evento è completo.

Ad esempio, nel 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!' });
  }());
});

Un errore può essere dovuto a un solo errore 404, che potrebbe non essere stato importante per te, quindi potrebbe valere comunque la pena copiare alcune risposte in una cache come sopra.

Reazione al clic

L'interfaccia utente che mostra l'avanzamento del download e il risultato è cliccabile. L'evento backgroundfetchclick nel service worker ti consente di reagire a questa situazione. Come sopra, event.registration sarà la registrazione per il recupero in background.

Di solito in questo evento si apre 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 faceva erroneamente riferimento al recupero in background come uno "standard web". Al momento l'API non rientra nel canale standard. La specifica è disponibile in WICG sotto forma di bozza di report sul gruppo della community.