لمحة عن ميزة "الجلب في الخلفية"

في عام 2015، أطلقنا ميزة "المزامنة في الخلفية" التي تسمح لعامل الخدمة بالتريث في تنفيذ المهام إلى أن يتصل المستخدم بالإنترنت. وهذا يعني أنّه يمكن للمستخدم كتابة رسالة والنقر على "إرسال" ومغادرة الموقع الإلكتروني مع العِلم أنّه سيتم إرسال الرسالة الآن أو عندما يصبح الاتصال بالإنترنت متاحًا.

هذه ميزة مفيدة، ولكنها تتطلّب أن يكون عامل الخدمة نشطًا طوال مدة الاسترداد. لا يشكّل ذلك مشكلة في المهام القصيرة، مثل إرسال رسالة، ولكن إذا استغرقت المهمة وقتًا طويلاً جدًا، سيوقف المتصفّح الخدمة العاملة، وإلا سيشكّل ذلك خطرًا على خصوصية المستخدم و البطارية.

ماذا لو أردت تنزيل محتوى قد يستغرق وقتًا طويلاً، مثل فيلم أو ملفات بودكاست أو مستويات لعبة؟ وهذا هو الغرض من ميزة أداة استرجاع البيانات في الخلفية.

تتوفّر ميزة "جلب البيانات في الخلفية" تلقائيًا منذ الإصدار 74 من Chrome.

في ما يلي عرض توضيحي سريع مدته دقيقتان يعرض الحالة التقليدية للتطبيقات مقارنةً باستخدام ميزة "جلب البيانات في الخلفية":

جرِّب العرض الترويجي بنفسك وتصفَّح الرمز.

آلية العمل

تعمل ميزة "استرداد البيانات في الخلفية" على النحو التالي:

  1. يمكنك أن تطلب من المتصفّح تنفيذ مجموعة من عمليات الجلب في الخلفية.
  2. يُجلب المتصفّح هذه العناصر ويعرض مستوى التقدّم للمستخدم.
  3. بعد اكتمال عملية الجلب أو تعذّرها، يفتح المتصفّح الخدمة العاملة ويُطلق حدثًا لإعلامك بما حدث. يمكنك هنا تحديد ما إذا كنت تريد اتّخاذ أي إجراء بشأن الردود.

إذا أغلق المستخدم صفحات موقعك الإلكتروني بعد الخطوة 1، لا بأس بذلك، سيستمر التنزيل. وبما أنّ عملية الجلب مرئية بشكل كبير ويمكن إيقافها بسهولة، لا داعي للقلق بشأن الخصوصية في حال كانت مهمة المزامنة في الخلفية طويلة جدًا. وبما أنّ الخدمة العاملة لا تعمل باستمرار، لا داعي للقلق بشأن إمكانية إساءة استخدام النظام، مثل تعدين عملة البيتكوين في الخلفية.

في بعض الأنظمة الأساسية (مثل Android)، من الممكن أن يتم إغلاق المتصفّح بعد الخطوة 1، لأنّ المتصفّح يمكنه تسليم عملية الجلب إلى نظام التشغيل.

إذا بدأ المستخدم عملية التنزيل عندما يكون غير متصل بالإنترنت، أو إذا انقطع الاتصال بالإنترنت أثناء التنزيل، سيتم إيقاف عملية الالتقاط في الخلفية مؤقتًا واستئنافها لاحقًا.

واجهة برمجة التطبيقات

ميزة "اكتشاف المحتوى"

وكما هو الحال مع أي ميزة جديدة، عليك معرفة ما إذا كان المتصفّح متوافقًا معها. بالنسبة إلى ميزة "الاسترجاع في الخلفية"، يمكنك تنفيذ ما يلي:

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

بدء عملية استرجاع البيانات في الخلفية

تعتمد واجهة برمجة التطبيقات الرئيسية على تسجيل مشغّل خدمات، لذا تأكَّد من تسجيل مشغّل خدمات أولاً. بعد ذلك:

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 ثلاث وسيطات:

المعلمات
id يحدِّد string
بشكلٍ فريد عملية الجلب هذه التي تعمل في الخلفية.

سيرفض backgroundFetch.fetch الطلب إذا كان رقم التعريف يتطابق مع عملية جلب حالية في background.

requests Array<Request|string>
العناصر التي يجب جلبها سيتم التعامل مع السلاسل على أنّها عناوين URL، وتحويلها إلى Request من خلال new Request(theString).

يمكنك جلب عناصر من مصادر أخرى طالما أنّ الموارد تسمح بذلك من خلال CORS.

ملاحظة: لا يتيح Chrome حاليًا الطلبات التي تتطلّب إجراء فحص مسبق لبروتوكول CORS.

options عنصر قد يتضمّن ما يلي:
options.title string
عنوان للمتصفّح لعرضه مع مستوى التقدّم
options.icons Array<IconDefinition>
مصفوفة من العناصر التي تحتوي على سمات src وsize وtype
options.downloadTotal number
الحجم الإجمالي لنصّ الردود (بعد إزالة ضغطها باستخدام برنامج gzip)

على الرغم من أنّ هذا الإجراء اختياري، ننصحك بشدة بتقديمه. ويُستخدَم لإعلام المستخدم بحجم عملية التنزيل وتقديم معلومات عن مستوى التقدّم. وفي حال عدم تقديم هذه المعلومات، سيُعلم المتصفّح المستخدم بأنّ الحجم غير معروف، ونتيجةً لذلك، قد يلغي المستخدم عملية التنزيل.

إذا تجاوز عدد عمليات تنزيل البيانات في الخلفية العدد المحدَّد هنا، سيتم إلغاء هذه العمليات. ليس هناك مشكلة إذا كان حجم الملف الذي يتم تنزيله أصغر من downloadTotal، لذا إذا لم تكن متأكّدًا من حجم الملف الإجمالي الذي سيتم تنزيله، من الأفضل أن تتوخى الحذر.

يعرض backgroundFetch.fetch وعدًا يتم حلّه باستخدام BackgroundFetchRegistration. سأتناول تفاصيل ذلك لاحقًا. يتم رفض الوعد إذا أوقف المستخدم عمليات التنزيل أو إذا كانت إحدى المَعلمات المقدَّمة غير صالحة.

من خلال تقديم العديد من الطلبات لإجراء عملية جلب واحدة في الخلفية، يمكنك دمج العناصر التي تُعدّ من الناحية المنطقية عنصرًا واحدًا للمستخدم. على سبيل المثال، يمكن تقسيم فيلم إلى آلاف الموارد (عادةً ما يكون ذلك باستخدام MPEG-DASH)، ويُمكن أن يتضمّن موارد إضافية، مثل الصور. يمكن أن يشمل مستوى من مستويات اللعبة العديد من موارد JavaScript والصور والصوت. ولكن بالنسبة إلى المستخدم، ما يهمّه هو "الفيلم" أو "المستوى".

الحصول على عملية استرجاع بيانات حالية في الخلفية

يمكنك الحصول على عملية جلب حالية في الخلفية على النحو التالي:

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

…من خلال تمرير معرّف عملية الجلب في الخلفية التي تريدها. تعرض الدالة get القيمة undefined إذا لم يكن هناك عملية جلب نشطة في الخلفية باستخدام هذا المعرّف.

يُعتبر طلب الجلب في الخلفية "نشطًا" من لحظة تسجيله إلى أن يتمكّن من إكمال المهمة بنجاح أو يتعذّر عليه إكمالها أو يتم إيقافه.

يمكنك الحصول على قائمة بجميع عمليات الجلب النشطة في الخلفية باستخدام getIds:

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

عمليات تسجيل استرجاع البيانات في الخلفية

يحتوي BackgroundFetchRegistration (bgFetch في الأمثلة أعلاه) على ما يلي:

أماكن إقامة
id string
رقم تعريف عملية الجلب في الخلفية
uploadTotal number
عدد وحدات البايت التي سيتم إرسالها إلى الخادم.
uploaded number
عدد وحدات البايت التي تم إرسالها بنجاح
downloadTotal number
القيمة المقدَّمة عند تسجيل عملية الجلب في الخلفية، أو القيمة صفر
downloaded number
عدد وحدات البايت التي تم استلامها بنجاح

وقد تنخفض هذه القيمة. على سبيل المثال، إذا انقطع الاتصال ولم يكن بإمكانك استئناف عملية التنزيل، سيعيد المتصفّح جلب هذا المورد من البداية.

result

يجب استخدام إحدى السمات التالية:

  • "" - عملية الجلب في الخلفية نشطة، لذلك لم يتم الحصول على أي نتيجة بعد.
  • "success" - تم استرجاع البيانات من الخلفية بنجاح.
  • "failure" - تعذّر جلب البيانات في الخلفية. لا تظهر هذه القيمة إلا عند تعذُّر استرجاع البيانات في الخلفية تمامًا، لأنّ المتصفح لا يمكنه إعادة المحاولة أو استئناف العمل.
failureReason

يجب استخدام إحدى السمات التالية:

  • "" - لم يحدث أي خطأ في عملية الجلب في الخلفية.
  • "aborted" – أوقف المستخدم عملية الجلب في الخلفية، أو تم استدعاء abort().
  • "bad-status" - كانت حالة إحدى الاستجابات غير جيدة، مثلاً 404.
  • "fetch-error" - تعذّر أحد عمليات الجلب لسبب آخر، مثل CORS أو MIX أو استجابة جزئية غير صالحة أو خطأ عام في الشبكة لعملية جلب لا يمكن إعادة تجربتها.
  • "quota-exceeded" - تم بلوغ مساحة التخزين المتوفّرة أثناء عملية الجلب في الخلفية.
  • "download-total-exceeded" - تم تجاوز القيمة المقدَّمة لسمة downloadTotal
recordsAvailable boolean
هل يمكن الوصول إلى الطلبات أو الردود الأساسية؟

بعد ضبط هذا الخيار على "false"، لا يمكن استخدام match وmatchAll.

الطُرق
abort() عرض القيمة Promise<boolean>
إيقاف جلب البيانات في الخلفية

يتم حلّ الوعد الذي تم إرجاعه بقيمة صحيحة إذا تم إلغاء عملية الجلب بنجاح.

matchAll(request, opts) عرض Promise<Array<BackgroundFetchRecord>>
الطلبات والردود

وسيطات هذه الوظيفة هي نفسها المُستخدَمة في واجهة برمجة التطبيقات للمخزن المؤقت. يؤدي الطلب بدون وسيطات إلى عرض وعد لجميع السجلات.

انظر أدناه للحصول على مزيد من التفاصيل.

match(request, opts) تعرِض القيمة Promise<BackgroundFetchRecord>
كما هو موضّح أعلاه، ولكن يتم حلّها باستخدام أول مطابقة.
الفعاليات
progress يتم تشغيله عند تغيير أيّ من uploaded أو downloaded أو result أو failureReason.

تتبُّع مستوى التقدّم

ويمكن إجراء ذلك من خلال الحدث progress. تذكَّر أنّ downloadTotal هي أي قيمة قدمتها، أو 0 إذا لم تقدّم أي قيمة.

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}%`);
});

تلقّي الطلبات والردود

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 هو BackgroundFetchRecord، ويظهر على النحو التالي:

أماكن إقامة
request Request
الطلب الذي تم تقديمه.
responseReady Promise<Response>
الردّ الذي تم استرجاعه.

تأخّر الردّ عن الموعد المحدّد لأنّه قد لا يكون قد تم استلامه بعد. سيتم رفض الوعد في حال تعذّر استرجاع البيانات.

أحداث مشغِّل الخدمة

الفعاليات
backgroundfetchsuccess تم جلب كل البيانات بنجاح.
backgroundfetchfailure تعذّر جلب بيانات واحدة أو أكثر.
backgroundfetchabort تعذّر جلب بيانات واحدة أو أكثر.

ولا يكون هذا مفيدًا إلا إذا كنت تريد تنظيف البيانات ذات الصلة.

backgroundfetchclick نقر المستخدم على واجهة مستخدم مستوى التقدّم في عملية التنزيل.

تتضمّن عناصر الأحداث ما يلي:

أماكن إقامة
registration BackgroundFetchRegistration
الطُرق
updateUI({ title, icons }) يتيح لك تغيير العنوان أو الرموز التي ضبطتها في البداية. هذا الإجراء اختياري، ولكنه يتيح لك تقديم سياق إضافي إذا لزم الأمر. يمكنك إجراء ذلك *مرة واحدة* فقط أثناء حدثَي backgroundfetchsuccess وbackgroundfetchfailure.

التفاعل مع حالات النجاح أو الفشل

لقد سبق أن رأينا الحدث progress، ولكنّه لا يكون مفيدًا إلا عندما يكون لدى المستخدِم صفحة مفتوحة على موقعك الإلكتروني. تتمثل الفائدة الرئيسية من ميزة "الاسترداد في الخلفية" في مواصلة عمل التطبيقات بعد مغادرة المستخدم الصفحة أو حتى إغلاق المتصفّح.

إذا اكتملت عملية الجلب في الخلفية بنجاح، سيتلقّى عامل الخدمة الحدث backgroundfetchsuccess، وسيكون event.registration هو تسجيل الجلب في الخلفية.

بعد هذا الحدث، لن يعود بإمكانك الوصول إلى الطلبات والردود التي تم جلبها، لذا إذا أردت الاحتفاظ بها، عليك نقلها إلى مكان آخر، مثل واجهة برمجة التطبيقات الخاصة بالخزين المؤقت.

كما هو الحال مع معظم أحداث الخدمة، استخدِم event.waitUntil حتى يعرف العامل في الخدمة وقت اكتمال الحدث.

على سبيل المثال، في 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!' });
  }());
});

قد يكون سبب تعذُّر الربط هو استجابة واحدة من النوع 404، والتي قد لا تكون مهمة بالنسبة إليك، لذا قد يظل من المفيد نسخ بعض الاستجابات إلى ذاكرة تخزين مؤقت كما هو موضّح أعلاه.

التفاعل مع النقرات

يمكن النقر على واجهة المستخدم التي تعرض مستوى تقدّم التنزيل والنتيجة. يتيح لك حدث backgroundfetchclick في عامل الخدمة التفاعل مع ذلك. كما هو موضح أعلاه، سيكون event.registration هو عملية تسجيل الخلفية.

الإجراء الشائع لتنفيذ هذا الحدث هو فتح نافذة:

addEventListener('backgroundfetchclick', (event) => {
  const bgFetch = event.registration;

  if (bgFetch.result === 'success') {
    clients.openWindow('/latest-podcasts');
  } else {
    clients.openWindow('/download-progress');
  }
});

مراجع إضافية

تصحيح: أشار إصدار سابق من هذه المقالة بشكل خاطئ إلى ميزة "جلب التطبيقات في الخلفية" على أنّها "معيار ويب". لا تتوفّر واجهة برمجة التطبيقات حاليًا في مسار المعايير، ويمكن العثور على المواصفات في WICG كمشروع مسودة تقرير مجموعات المنتدى.