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

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

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

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

تتوفّر ميزة Background Fetch تلقائيًا منذ الإصدار 74 من Chrome.

إليك عرض توضيحي سريع مدته دقيقتان يعرض الحالة التقليدية للأشياء مقارنةً باستخدام Background Fetch:

آلية العمل

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

  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 إذا كان رقم التعريف يطابق عملية جلب بيانات حالية في الخلفية.

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>
إلغاء عملية الجلب في الخلفية.

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

matchAll(request, opts) تعرض هذه السمة Promise<Array<BackgroundFetchRecord>>
طلبات البحث والردود.

تكون الوسيطات هنا هي نفسها الخاصة بواجهة Cache API. يؤدي طلب الإجراء بدون وسيطات إلى عرض وعد بجميع السجلات.

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

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 من قبل، ولكنّه لا يكون مفيدًا إلا عندما يفتح المستخدم صفحة من موقعك الإلكتروني. تتمثّل الفائدة الرئيسية من ميزة &quot;الجلب في الخلفية&quot; في أنّ العمليات تستمر حتى بعد أن يغادر المستخدم الصفحة أو يغلق المتصفّح.

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

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

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

على سبيل المثال، في عامل الخدمة:

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');
  }
});

مراجع إضافية

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