Data publikacji: 20 lutego 2025 r.
Pobieranie dużych modeli AI w sposób niezawodny jest trudnym zadaniem. Jeśli użytkownicy stracą połączenie z internetem lub zamkną Twoją witrynę lub aplikację internetową, utracą częściowo pobrane pliki modeli i po powrocie na stronę będą musieli zacząć od nowa. Korzystając z Background Fetch API jako ulepszenia stopniowego, możesz znacznie poprawić wygodę użytkowników.
Rejestrowanie skryptu service worker
Interfejs Background Fetch API wymaga, aby aplikacja zarejestrowała skrypt 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);
});
}
Wywołanie pobrania w tle
Podczas pobierania przeglądarka wyświetla użytkownikowi postęp i metodę anulowania pobierania. Po zakończeniu pobierania przeglądarka uruchamia usługę, a aplikacja może podjąć działanie na podstawie otrzymanej odpowiedzi.
Interfejs Background Fetch API może nawet przygotować pobieranie do rozpoczęcia w trybie offline. Po ponownym połączeniu się z siecią pobieranie rozpocznie się natychmiast. Jeśli użytkownik przejdzie w tryb offline, proces zostanie wstrzymany do czasu, gdy ponownie połączy się z internetem.
W tym przykładzie użytkownik klika przycisk, aby pobrać Gemma 2B. Zanim pobieramy model, sprawdzamy, czy został on wcześniej pobrany i zapisany w pamięci podręcznej, aby nie używać niepotrzebnych zasobów. Jeśli nie ma go w pamięci podręcznej, rozpoczynamy pobieranie w tle.
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),
});
}
});
Funkcja getResourceSize()
zwraca rozmiar w bajtach pobranego pliku. Aby to zrobić, prześlij 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;
}
};
Postęp pobierania raportu
Po rozpoczęciu pobierania w tle przeglądarka zwraca wartość BackgroundFetchRegistration
.
Możesz go użyć, aby poinformować użytkownika o postępie pobierania za pomocą zdarzenia 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}`);
});
Powiadamianie użytkowników i klienta o zakończeniu pobierania
Gdy pobranie w tle się powiedzie, worker usługi aplikacji otrzyma zdarzenie backgroundfetchsuccess
.
Ten kod jest zawarty w usługach działających w tle. wywołanie updateUI()
pod koniec pozwala zaktualizować interfejs przeglądarki, aby powiadomić użytkownika o udanym pobraniu w tle. Na koniec poinformuj klienta o zakończonym pobieraniu, na przykład za pomocą 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,
});
}
});
})(),
);
});
Odbieranie wiadomości od usługi w tle
Aby otrzymać na kliencie wysłaną wiadomość o udanym zakończeniu pobierania, nasłuchuj zdarzenia message
. Po otrzymaniu wiadomości od service workera możesz pracować z modelem AI i przechowywać go za pomocą interfejsu Cache API.
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);
}
});
Anulowanie pobierania w tle
Aby umożliwić użytkownikowi anulowanie trwającego pobierania, użyj metody abort()
interfejsu BackgroundFetchRegistration
.
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
return;
}
await bgFetch.abort();
Zapisz model w pamięci podręcznej
Przechowuj pobrane modele, aby użytkownicy pobierali je tylko raz. Chociaż interfejs Background Fetch API poprawia komfort pobierania, zawsze staraj się używać jak najmniejszego modelu AI po stronie klienta.
Te interfejsy API pomogą Ci zapewnić użytkownikom lepsze wrażenia z korzystania z AI po stronie klienta.
Prezentacja
Pełne wdrożenie tego podejścia znajdziesz w demo i w kodzie źródłowym.

Podziękowania
Ten przewodnik został sprawdzony przez Françoisa Beauforta, Andre Bandarrę, Sebastiana Benza, Maud Nalpas i Alexandrę Klepper.