Dipublikasikan: 20 Februari 2025
Mendownload model AI besar secara andal adalah tugas yang sulit. Jika pengguna kehilangan koneksi internet atau menutup situs atau aplikasi web Anda, mereka akan kehilangan file model yang didownload sebagian dan harus memulai ulang saat kembali ke halaman Anda. Dengan menggunakan Background Fetch API sebagai peningkatan progresif, Anda dapat meningkatkan pengalaman pengguna secara signifikan.
Mendaftarkan pekerja layanan
Background Fetch API mengharuskan aplikasi Anda untuk mendaftarkan pekerja layanan.
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);
});
}
Memicu pengambilan latar belakang
Saat mengambil, browser akan menampilkan progres kepada pengguna dan memberi mereka metode untuk membatalkan download. Setelah download selesai, browser akan memulai pekerja layanan dan aplikasi dapat mengambil tindakan dengan respons.
Background Fetch API bahkan dapat menyiapkan pengambilan untuk dimulai saat offline. Segera setelah pengguna terhubung kembali, download akan dimulai. Jika pengguna offline, proses akan dijeda hingga pengguna kembali online.
Dalam contoh berikut, pengguna mengklik tombol untuk mendownload Gemma 2B. Sebelum mengambil, kita memeriksa apakah model telah didownload dan di-cache sebelumnya, sehingga kita tidak menggunakan resource yang tidak perlu. Jika tidak di-cache, kita akan memulai pengambilan data di latar belakang.
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),
});
}
});
Fungsi getResourceSize() menampilkan ukuran byte download. Anda dapat
menerapkannya dengan membuat
permintaan 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;
}
};
Progres download laporan
Setelah pengambilan latar belakang dimulai, browser akan menampilkan
BackgroundFetchRegistration.
Anda dapat menggunakan ini untuk memberi tahu pengguna tentang progres download, dengan peristiwa
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}`);
});
Memberi tahu pengguna dan klien tentang penyelesaian pengambilan
Jika pengambilan di latar belakang berhasil, pekerja layanan aplikasi Anda akan menerima
peristiwa backgroundfetchsuccess.
Kode berikut disertakan dalam service worker. Panggilan
updateUI()
di dekat akhir memungkinkan Anda memperbarui antarmuka browser untuk memberi tahu pengguna tentang
pengambilan latar belakang yang berhasil. Terakhir, beri tahu klien
tentang download yang telah selesai, misalnya, menggunakan
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,
});
}
});
})(),
);
});
Menerima pesan dari pekerja layanan
Untuk menerima pesan sukses yang dikirim tentang download yang selesai di klien, dengarkan peristiwa
message. Setelah menerima pesan dari pekerja layanan, Anda dapat menggunakan
model AI dan menyimpannya dengan 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);
}
});
Membatalkan pengambilan latar belakang
Agar pengguna dapat membatalkan download yang sedang berlangsung, gunakan metode abort() dari
BackgroundFetchRegistration.
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
return;
}
await bgFetch.abort();
Meng-cache model
Menyimpan model yang didownload dalam cache, sehingga pengguna Anda hanya mendownload model satu kali. Meskipun Background Fetch API meningkatkan pengalaman download, Anda harus selalu berupaya menggunakan model terkecil yang memungkinkan dalam AI sisi klien.
Bersama-sama, API ini membantu Anda menciptakan pengalaman AI sisi klien yang lebih baik bagi pengguna.
Demo
Anda dapat melihat implementasi lengkap pendekatan ini di demo dan kode sumbernya.
Ucapan terima kasih
Panduan ini ditinjau oleh François Beaufort, Andre Bandarra, Sebastian Benz, Maud Nalpas, dan Alexandra Klepper.