مزامنة خلفية مربع العمل

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

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

تم تصميم أداة "مزامنة الخلفية في Workbox" لتسهيل استخدام واجهة برمجة التطبيقات BackgroundSync API ودمج استخدامها مع وحدات Workbox الأخرى. إضافةً إلى ذلك، ينفّذ هذا الإجراء استراتيجية احتياطية للمتصفّحات التي لم تنفِّذ بعد هذا الإجراء في الخلفية.

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

الاستخدام الأساسي

إنّ أسهل طريقة لاستخدام ميزة "المزامنة في الخلفية" هي استخدام Plugin الذي سيعمل تلقائيًا على إضافة الطلبات التي تعذّر تنفيذها إلى قائمة الانتظار وإعادة المحاولة عند تنشيط أحداث sync المستقبلية.

import {BackgroundSyncPlugin} from 'workbox-background-sync';
import {registerRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {
  maxRetentionTime: 24 * 60, // Retry for max of 24 Hours (specified in minutes)
});

registerRoute(
  /\/api\/.*\/*.json/,
  new NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
  'POST'
);

يتم تثبيت BackgroundSyncPlugin في رد اتصال المكوّن الإضافي fetchDidFail، ولا يتم استدعاء fetchDidFail إلا في حال تقديم استثناء، ويرجع السبب في ذلك على الأرجح إلى حدوث عطل في الشبكة. يعني ذلك أنّه لن تتم إعادة محاولة إرسال الطلبات في حال تلقّي ردّ يتضمّن حالة الخطأ 4xx أو 5xx. إذا أردت إعادة محاولة تنفيذ جميع الطلبات التي تؤدي إلى ظهور الحالة 5xx مثلاً، يمكنك إجراء ذلك من خلال إضافة مكوّن fetchDidSucceed إضافي إلى استراتيجيتك:

const statusPlugin = {
  fetchDidSucceed: ({response}) => {
    if (response.status >= 500) {
      // Throwing anything here will trigger fetchDidFail.
      throw new Error('Server error.');
    }
    // If it's not 5xx, use the response as-is.
    return response;
  },
};

// Add statusPlugin to the plugins array in your strategy.

الاستخدام المتقدّم

توفّر "مزامنة الخلفية في Workbox" أيضًا فئة Queue يمكنك إثباتها وإضافة الطلبات التي تعذّر تنفيذها إليها. يتم تخزين الطلبات التي تعذّر تنفيذها في IndexedDB وتتم إعادة محاولة استخدامها عندما يعتقد المتصفِّح أنّه تمت استعادة الاتصال (أي عندما يتلقّى حدث المزامنة).

إنشاء قائمة انتظار

لإنشاء قائمة انتظار مزامنة الخلفية لـ Workbox، يجب إنشاؤها باسم قائمة الانتظار (يجب أن يكون فريدًا بالنسبة إلى origin):

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

يُستخدم اسم قائمة المحتوى التالي كجزء من اسم العلامة التي تتم إضافة register() إليها بواسطة علامة SyncManager العالمية. كما يُستخدم كاسم مخزن الكائنات لقاعدة البيانات المفهرسة.

إضافة طلب إلى قائمة الانتظار

بمجرد إنشاء مثيل قائمة الانتظار، يمكنك إضافة الطلبات التي تعذّر تنفيذها إليها. لقد أضفت الطلب الذي تعذّر تحميله من خلال استدعاء طريقة .pushRequest(). على سبيل المثال، تكتشف التعليمة البرمجية التالية أي طلبات تفشل وتضيفها إلى قائمة الانتظار:

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

self.addEventListener('fetch', event => {
  // Add in your own criteria here to return early if this
  // isn't a request that should use background sync.
  if (event.request.method !== 'POST') {
    return;
  }

  const bgSyncLogic = async () => {
    try {
      const response = await fetch(event.request.clone());
      return response;
    } catch (error) {
      await queue.pushRequest({request: event.request});
      return error;
    }
  };

  event.respondWith(bgSyncLogic());
});

وبعد إضافة الطلب إلى قائمة الانتظار، تتم إعادة محاولة إرسال الطلب تلقائيًا عندما يتلقّى مشغّل الخدمة حدث sync (الذي يحدث عندما يعتقد المتصفّح أنّه تمت استعادة الاتصال). ستعيد المتصفحات التي لا تتيح تشغيل واجهة برمجة التطبيقات BackgroundSync API إعادة محاولة إضافة قائمة الانتظار في كل مرة يتم فيها تشغيل عامل الخدمة. يتطلب ذلك تشغيل الصفحة التي تتحكم في مشغّل الخدمات، لذا لن تكون فعالة تمامًا.

اختبار المزامنة في الخلفية لإطار العمل

للأسف، قد يكون اختبار BackgroundSyncيّة غير بديهية إلى حدٍ ما، وذلك لعدة أسباب.

في ما يلي أفضل طريقة لاختبار عملية التنفيذ:

  1. حمِّل الصفحة وسجِّل مشغّل الخدمات.
  2. أوقِف شبكة الكمبيوتر أو أوقِف خادم الويب.
    • لا تستخدِم "أدوات مطوّري برامج Chrome" بلا إنترنت. ويؤثر مربّع الاختيار بلا اتصال بالإنترنت في "أدوات مطوري البرامج" في الطلبات الواردة من الصفحة فقط. ستستمر طلبات مشغلي الخدمة في الاستجابة.
  3. يمكنك إجراء طلبات الشبكة التي يجب وضعها في قائمة الانتظار باستخدام Workbox Background Sync.
    • يمكنك التأكّد من أنّ الطلبات قد تمّت إضافتها إلى قائمة الانتظار من خلال الاطّلاع على Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests.
  4. الآن شغِّل الشبكة أو خادم الويب.
  5. يمكنك فرض حدث sync مبكر بالانتقال إلى Chrome DevTools > Application > Service Workers، وإدخال اسم علامة workbox-background-sync:<your queue name> حيث يجب أن يكون <your queue name> هو اسم قائمة الانتظار التي يتم إعدادها، ثم النقر على الزر "مزامنة".

    مثال على زر &quot;المزامنة&quot; في &quot;أدوات مطوري البرامج في Chrome&quot;

  6. من المفترض أن تشاهد طلبات الشبكة تمر للطلبات التي تعذّر تنفيذها، ويجب أن تكون بيانات IndexedDB فارغة الآن نظرًا لإعادة تشغيل الطلبات بنجاح.

الأنواع

BackgroundSyncPlugin

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

أماكن إقامة

Queue

فئة لإدارة تخزين الطلبات الفاشلة في IndexedDB وإعادة المحاولة لاحقًا. يمكن ملاحظة جميع أجزاء عملية التخزين وإعادة التشغيل عن طريق عمليات معاودة الاتصال.

أماكن إقامة

  • الدالة الإنشائية

    void

    تنشئ مثيلاً من قائمة الانتظار بالخيارات المحددة

    تبدو الدالة constructor على النحو التالي:

    (name: string,options?: QueueOptions)=> {...}

    • اسم

      سلسلة

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

    • الخيارات

      QueueOptions اختيارية

  • اسم

    سلسلة

  • getAll

    void

    عرض جميع الإدخالات التي لم تنته صلاحيتها (لكل maxRetentionTime). تتم إزالة أي إدخالات منتهية الصلاحية من قائمة الانتظار.

    تبدو الدالة getAll على النحو التالي:

    ()=> {...}

    • returns

      Promise<QueueEntry[]>

  • popRequest

    void

    إزالة الطلب الأخير وعرضه في قائمة الانتظار (بالإضافة إلى طابعه الزمني وأي بيانات وصفية) ويكون الكائن الذي يتم عرضه على النحو التالي: {request, timestamp, metadata}.

    تبدو الدالة popRequest على النحو التالي:

    ()=> {...}

    • returns

      Promise<QueueEntry>

  • pushRequest

    void

    لتخزين الطلب الذي تم تمريره في IndexedDB (مع طابعه الزمني وأي بيانات وصفية) في نهاية قائمة الانتظار.

    تبدو الدالة pushRequest على النحو التالي:

    (entry: QueueEntry)=> {...}

    • الإدخال

      QueueEntry

    • returns

      Promise<void>

  • registerSync

    void

    تسجِّل حدث مزامنة باستخدام علامة فريدة لهذا المثيل.

    تبدو الدالة registerSync على النحو التالي:

    ()=> {...}

    • returns

      Promise<void>

  • replayRequests

    void

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

    تبدو الدالة replayRequests على النحو التالي:

    ()=> {...}

    • returns

      Promise<void>

  • shiftRequest

    void

    يزيل الطلب الأول ويعرضه في قائمة الانتظار (بالإضافة إلى طابعه الزمني وأي بيانات وصفية). ويكون الكائن الذي يتم عرضه على النحو التالي: {request, timestamp, metadata}.

    تبدو الدالة shiftRequest على النحو التالي:

    ()=> {...}

    • returns

      Promise<QueueEntry>

  • الحجم

    void

    لعرض عدد الإدخالات الموجودة في قائمة الانتظار. تجدر الإشارة إلى أنّ الإدخالات المنتهية الصلاحية (لكل maxRetentionTime) يتم تضمينها أيضًا في هذا العدد.

    تبدو الدالة size على النحو التالي:

    ()=> {...}

    • returns

      وعد<الرقم>

  • unshiftRequest

    void

    لتخزين الطلب الذي تم تمريره في IndexedDB (مع طابعه الزمني وأي بيانات وصفية) في بداية قائمة الانتظار.

    تبدو الدالة unshiftRequest على النحو التالي:

    (entry: QueueEntry)=> {...}

    • الإدخال

      QueueEntry

    • returns

      Promise<void>

QueueOptions

أماكن إقامة

  • forceSyncFallback

    منطقية اختيارية

  • maxRetentionTime

    الرقم اختياري

  • onSync

    OnSyncCallback اختياري

QueueStore

فئة لإدارة طلبات التخزين من قائمة انتظار في IndexedDB، تمت فهرستها حسب اسم قائمة الانتظار لتسهيل الوصول إليها.

لن يحتاج معظم المطورين إلى الوصول إلى هذا الصف مباشرةً؛ فإنه يتم عرضه في حالات الاستخدام المتقدمة.

أماكن إقامة

  • الدالة الإنشائية

    void

    تربط هذا المثيل بمثيل قائمة الانتظار، بحيث يمكن تحديد الإدخالات المضافة من خلال اسم قائمة الانتظار الخاصة بها.

    تبدو الدالة constructor على النحو التالي:

    (queueName: string)=> {...}

    • queueName

      سلسلة

  • deleteEntry

    void

    يؤدي هذا الإجراء إلى حذف إدخال رقم التعريف المحدّد.

    تحذير: لا تضمن هذه الطريقة أن يكون الإدخال الذي تم حذفه ينتمي إلى هذه قائمة الانتظار (أي يتطابق مع queueName). غير أن هذا القيد مقبول لأن هذا الفئة غير علنية. قد يؤدي إجراء فحص إضافي إلى جعل هذه الطريقة أبطأ مما ينبغي أن تكون.

    تبدو الدالة deleteEntry على النحو التالي:

    (id: number)=> {...}

    • id

      الرقم

    • returns

      Promise<void>

  • getAll

    void

    عرض جميع الإدخالات في المتجر التي تتطابق مع queueName.

    تبدو الدالة getAll على النحو التالي:

    ()=> {...}

    • returns

      Promise<QueueStoreEntry[]>

  • popEntry

    void

    لإزالة آخر إدخال في قائمة الانتظار يتطابق مع queueName وعرضه.

    تبدو الدالة popEntry على النحو التالي:

    ()=> {...}

    • returns

      Promise<QueueStoreEntry>

  • pushEntry

    void

    إلحاق آخر إدخال في قائمة الانتظار.

    تبدو الدالة pushEntry على النحو التالي:

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • الإدخال

      UnidentifiedQueueStoreEntry

    • returns

      Promise<void>

  • shiftEntry

    void

    لإزالة أول إدخال في قائمة الانتظار يتطابق مع queueName وإعادته.

    تبدو الدالة shiftEntry على النحو التالي:

    ()=> {...}

    • returns

      Promise<QueueStoreEntry>

  • الحجم

    void

    تعرض عدد الإدخالات في المتجر التي تتطابق مع queueName.

    تبدو الدالة size على النحو التالي:

    ()=> {...}

    • returns

      وعد<الرقم>

  • unshiftEntry

    void

    إضافة إدخال أولاً في قائمة الانتظار

    تبدو الدالة unshiftEntry على النحو التالي:

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • الإدخال

      UnidentifiedQueueStoreEntry

    • returns

      Promise<void>

StorableRequest

فئة لتسهيل إنشاء تسلسل للطلبات وإلغاء تسلسلها بحيث يمكن تخزينها في IndexedDB.

لن يحتاج معظم المطورين إلى الوصول إلى هذا الصف مباشرةً؛ فإنه يتم عرضه في حالات الاستخدام المتقدمة.

أماكن إقامة

  • الدالة الإنشائية

    void

    تقبل كائن بيانات الطلب الذي يمكن استخدامه لإنشاء Request ولكن يمكن تخزينها أيضًا في IndexedDB.

    تبدو الدالة constructor على النحو التالي:

    (requestData: RequestData)=> {...}

    • requestData

      RequestData

      هو عنصر طلب لبيانات يتضمن url بالإضافة إلى أي سمات ذات صلة بـ [requestInit]https://fetch.spec.whatwg.org/#requestinit.

  • نسخة مماثلة

    void

    تنشئ هذه الدالة نسخة عميقة من المثيل وتعيدها.

    تبدو الدالة clone على النحو التالي:

    ()=> {...}

  • toObject

    void

    لعرض نسخة طبق الأصل من كائن _requestData في المثيلات.

    تبدو الدالة toObject على النحو التالي:

    ()=> {...}

    • returns

      RequestData

  • toRequest

    void

    لتحويل هذا المثيل إلى طلب.

    تبدو الدالة toRequest على النحو التالي:

    ()=> {...}

    • returns

      الطلب

  • fromRequest

    void

    لتحويل كائن "طلب" إلى كائن عادي يمكن استنساخه أو ترميزه بسلسلة JSON.

    تبدو الدالة fromRequest على النحو التالي:

    (request: Request)=> {...}

    • طلب

      الطلب