تاريخ النشر: 20 فبراير 2025
يُعدّ تنزيل نماذج الذكاء الاصطناعي الكبيرة بشكل موثوق مهمة صعبة. إذا فقد المستخدمون الاتصال بالإنترنت أو أغلقوا موقعك الإلكتروني أو تطبيق الويب، سيفقدون ملفات النماذج التي تم تنزيلها جزئيًا وسيكون عليهم البدء من جديد عند العودة إلى صفحتك. باستخدام واجهة برمجة التطبيقات Background Fetch API كتحسين تدريجي، يمكنك تحسين تجربة المستخدم بشكل كبير.
تسجيل مشغّل الخدمات
تتطلّب واجهة برمجة التطبيقات Background Fetch API أن يقوم تطبيقك بتسجيل مشغّل خدمات.
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);
  });
}
بدء عملية استرجاع البيانات في الخلفية
أثناء جلب المتصفّح للملف، يعرض للمستخدم مستوى تقدّم التنزيل ويتيح له طريقة لإلغاء عملية التنزيل. بعد اكتمال عملية التنزيل، يبدأ المتصفّح في تشغيل عامل الخدمة ويمكن للتطبيق اتّخاذ إجراء بشأن الاستجابة.
يمكن لواجهة برمجة التطبيقات Background Fetch API حتى إعداد عملية الجلب لبدء العمل بلا إنترنت. وبمجرد إعادة اتصال المستخدم، ستبدأ عملية التنزيل. إذا انقطع اتصال المستخدم بالإنترنت، تتوقّف العملية مؤقتًا إلى أن يعود المستخدم إلى الاتصال بالإنترنت.
في المثال التالي، ينقر المستخدم على زر لتنزيل Gemma 2B. قبل جلب النموذج، نتحقّق مما إذا كان قد تم تنزيله وتخزينه مؤقتًا من قبل، وبالتالي لا نستخدم موارد غير ضرورية. إذا لم يتم تخزينها مؤقتًا، نبدأ عملية الجلب في الخلفية.
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),
    });
  }
});
تعرض الدالة getResourceSize() حجم التنزيل بالبايت. يمكنك تنفيذ ذلك من خلال إرسال طلب 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;
  }
};
مستوى التقدّم في عملية تنزيل التقرير
بعد بدء عملية الجلب في الخلفية، يعرض المتصفّح
BackgroundFetchRegistration.
يمكنك استخدام هذا الإجراء لإعلام المستخدم بمستوى تقدّم عملية التنزيل، وذلك باستخدام حدث
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}`);
});
إشعار المستخدمين والعميل باكتمال عملية الاسترجاع
عندما تنجح عملية الاسترجاع في الخلفية، يتلقّى عامل الخدمة في تطبيقك حدث backgroundfetchsuccess.
يتم تضمين الرمز التالي في عامل الخدمة. يتيح لك استدعاء
updateUI()
بالقرب من النهاية تعديل واجهة المتصفّح لإعلام المستخدم بنجاح عملية الجلب في الخلفية. أخيرًا، أبلِغ العميل
بانتهاء عملية التنزيل، مثلاً باستخدام
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,
          });
        }
      });
    })(),
  );
});
تلقّي الرسائل من عامل الخدمة
لتلقّي رسالة النجاح المرسَلة بشأن عملية التنزيل المكتملة على الجهاز العميل، استمع إلى أحداث message. بعد تلقّي الرسالة من عامل الخدمة، يمكنك استخدام نموذج الذكاء الاصطناعي وتخزينه باستخدام واجهة برمجة التطبيقات 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);
  }
});
إلغاء عملية جلب البيانات في الخلفية
للسماح للمستخدم بإلغاء عملية تنزيل قيد التقدّم، استخدِم طريقة abort() في BackgroundFetchRegistration.
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
  return;
}
await bgFetch.abort();
تخزين النموذج مؤقتًا
تخزين النماذج التي تم تنزيلها مؤقتًا، كي لا يضطر المستخدمون إلى تنزيل النموذج أكثر من مرة على الرغم من أنّ واجهة برمجة التطبيقات Background Fetch API تحسّن تجربة التنزيل، عليك دائمًا الحرص على استخدام أصغر نموذج ممكن في الذكاء الاصطناعي من جهة العميل.
تساعدك واجهات برمجة التطبيقات هذه معًا في إنشاء تجربة أفضل مستندة إلى الذكاء الاصطناعي من جهة العميل للمستخدمين.
عرض توضيحي
يمكنك الاطّلاع على عملية تنفيذ كاملة لهذا الأسلوب في العرض التوضيحي ورمز المصدر.
الإقرارات
تمت مراجعة هذا الدليل من قِبل فرانسوا بوفورت و أندريه باندارا و سيباستيان بنز و مود نالباس و ألكسندرا كليبر.