Pubblicata il 20 febbraio 2025
Scaricare in modo affidabile modelli di IA di grandi dimensioni è un'attività impegnativa. Se gli utenti perdono la connessione a internet o chiudono il tuo sito web o la tua applicazione web, perdono i file del modello scaricati parzialmente e devono ricominciare da capo quando tornano alla tua pagina. Utilizzando l'API Background Fetch come miglioramento progressivo, puoi migliorare notevolmente l'esperienza utente.
Registra un service worker
L'API Background Fetch richiede che la tua app registri un service worker.
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
const registration = await navigator.serviceWorker.register('sw.js');
console.log('Service worker registered for scope', registration.scope);
});
}
Attivare un recupero in background
Durante il recupero, il browser mostra all'utente lo stato di avanzamento e gli offre un metodo per annullare il download. Al termine del download, il browser avvia il service worker e l'applicazione può intervenire con la risposta.
L'API Background Fetch può anche preparare l'avvio del recupero in modalità offline. Non appena l'utente si riconnette, il download inizia. Se l'utente viene offline, il processo viene messo in pausa finché non è di nuovo online.
Nell'esempio seguente, l'utente fa clic su un pulsante per scaricare Gemma 2B. Prima di eseguire il recupero, controlliamo se il modello è stato scaricato e memorizzato nella cache in precedenza, in modo da non utilizzare risorse non necessarie. Se non è memorizzata nella cache, avviamo il recupero in background.
const FETCH_ID = 'gemma-2b';
const MODEL_URL =
'https://storage.googleapis.com/jmstore/kaggleweb/grader/g-2b-it-gpu-int4.bin';
downloadButton.addEventListener('click', async (event) => {
// If the model is already downloaded, return it from the cache.
const modelAlreadyDownloaded = await caches.match(MODEL_URL);
if (modelAlreadyDownloaded) {
const modelBlob = await modelAlreadyDownloaded.blob();
// Do something with the model.
console.log(modelBlob);
return;
}
// The model still needs to be downloaded.
// Feature detection and fallback to classic `fetch()`.
if (!('BackgroundFetchManager' in self)) {
try {
const response = await fetch(MODEL_URL);
if (!response.ok || response.status !== 200) {
throw new Error(`Download failed ${MODEL_URL}`);
}
const modelBlob = await response.blob();
// Do something with the model.
console.log(modelBlob);
return;
} catch (err) {
console.error(err);
}
}
// The service worker registration.
const registration = await navigator.serviceWorker.ready;
// Check if there's already a background fetch running for the `FETCH_ID`.
let bgFetch = await registration.backgroundFetch.get(FETCH_ID);
// If not, start a background fetch.
if (!bgFetch) {
bgFetch = await registration.backgroundFetch.fetch(FETCH_ID, MODEL_URL, {
title: 'Gemma 2B model',
icons: [
{
src: 'icon.png',
size: '128x128',
type: 'image/png',
},
],
downloadTotal: await getResourceSize(MODEL_URL),
});
}
});
La funzione getResourceSize()
restituisce le dimensioni in byte del download. Puoi implementare questa funzionalità inviando una richiesta HEAD
.
const getResourceSize = async (url) => {
try {
const response = await fetch(url, { method: 'HEAD' });
if (response.ok) {
return response.headers.get('Content-Length');
}
console.error(`HTTP error: ${response.status}`);
return 0;
} catch (error) {
console.error('Error fetching content size:', error);
return 0;
}
};
Monitorare l'avanzamento del download dei report
Una volta avviato il recupero in background, il browser restituisce un valore BackgroundFetchRegistration
.
Puoi utilizzarlo per informare l'utente sull'avanzamento del download con l'evento
progress
.
bgFetch.addEventListener('progress', (e) => {
// There's no download progress yet.
if (!bgFetch.downloadTotal) {
return;
}
// Something went wrong.
if (bgFetch.failureReason) {
console.error(bgFetch.failureReason);
}
if (bgFetch.result === 'success') {
return;
}
// Update the user about progress.
console.log(`${bgFetch.downloaded} / ${bgFetch.downloadTotal}`);
});
Avvisare gli utenti e il client del completamento del recupero
Quando il recupero in background va a buon fine, il service worker dell'app riceve un evento
backgroundfetchsuccess
.
Il seguente codice è incluso nel service worker. La chiamata
updateUI()
nella parte finale ti consente di aggiornare l'interfaccia del browser per informare l'utente del recupero in background riuscito. Infine, informa il cliente del completamento del download, ad esempio utilizzando postMessage()
.
self.addEventListener('backgroundfetchsuccess', (event) => {
// Get the background fetch registration.
const bgFetch = event.registration;
event.waitUntil(
(async () => {
// Open a cache named 'downloads'.
const cache = await caches.open('downloads');
// Go over all records in the background fetch registration.
// (In the running example, there's just one record, but this way
// the code is future-proof.)
const records = await bgFetch.matchAll();
// Wait for the response(s) to be ready, then cache it/them.
const promises = records.map(async (record) => {
const response = await record.responseReady;
await cache.put(record.request, response);
});
await Promise.all(promises);
// Update the browser UI.
event.updateUI({ title: 'Model downloaded' });
// Inform the clients that the model was downloaded.
self.clients.matchAll().then((clientList) => {
for (const client of clientList) {
client.postMessage({
message: 'download-complete',
id: bgFetch.id,
});
}
});
})(),
);
});
Ricevere messaggi dal service worker
Per ricevere il messaggio di esito positivo inviato relativo al download completato sul client,
monitora gli eventi
message
. Una volta ricevuto il messaggio dal service worker, puoi utilizzare il modello di IA e memorizzarlo con l'API Cache.
navigator.serviceWorker.addEventListener('message', async (event) => {
const cache = await caches.open('downloads');
const keys = await cache.keys();
for (const key of keys) {
const modelBlob = await cache
.match(key)
.then((response) => response.blob());
// Do something with the model.
console.log(modelBlob);
}
});
Annullare un recupero in background
Per consentire all'utente di annullare un download in corso, utilizza il metodo abort()
di BackgroundFetchRegistration
.
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
return;
}
await bgFetch.abort();
Memorizza nella cache il modello
Memorizza nella cache i modelli scaricati, in modo che gli utenti scarichino il modello solo una volta. Sebbene l'API Background Fetch migliori l'esperienza di download, devi sempre cercare di utilizzare il modello più piccolo possibile nell'IA lato client.
Insieme, queste API ti aiutano a creare un'esperienza AI lato client migliore per i tuoi utenti.
Demo
Puoi vedere un'implementazione completa di questo approccio nella demo e nel relativo codice sorgente.

Ringraziamenti
Questa guida è stata esaminata da François Beaufort, Andre Bandarra, Sebastian Benz, Maud Nalpas e Alexandra Klepper.