عند إرسال البيانات إلى خادم ويب، ستفشل الطلبات أحيانًا. ربما يرجع السبب في ذلك إلى فقد المستخدم للاتصال، أو إلى تعطل الخادم، وفي كلتا الحالتين تحتاج غالبًا إلى محاولة إرسال الطلبات مرة أخرى لاحقًا.
وتمثل 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يّة غير بديهية إلى حدٍ ما، وذلك لعدة أسباب.
في ما يلي أفضل طريقة لاختبار عملية التنفيذ:
- حمِّل الصفحة وسجِّل مشغّل الخدمات.
- أوقِف شبكة الكمبيوتر أو أوقِف خادم الويب.
- لا تستخدِم "أدوات مطوّري برامج Chrome" بلا إنترنت. ويؤثر مربّع الاختيار بلا اتصال بالإنترنت في "أدوات مطوري البرامج" في الطلبات الواردة من الصفحة فقط. ستستمر طلبات مشغلي الخدمة في الاستجابة.
- يمكنك إجراء طلبات الشبكة التي يجب وضعها في قائمة الانتظار باستخدام Workbox Background Sync.
- يمكنك التأكّد من أنّ الطلبات قد تمّت إضافتها إلى قائمة الانتظار من خلال الاطّلاع على
Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests
.
- يمكنك التأكّد من أنّ الطلبات قد تمّت إضافتها إلى قائمة الانتظار من خلال الاطّلاع على
- الآن شغِّل الشبكة أو خادم الويب.
يمكنك فرض حدث
sync
مبكر بالانتقال إلىChrome DevTools > Application > Service Workers
، وإدخال اسم علامةworkbox-background-sync:<your queue name>
حيث يجب أن يكون<your queue name>
هو اسم قائمة الانتظار التي يتم إعدادها، ثم النقر على الزر "مزامنة".من المفترض أن تشاهد طلبات الشبكة تمر للطلبات التي تعذّر تنفيذها، ويجب أن تكون بيانات IndexedDB فارغة الآن نظرًا لإعادة تشغيل الطلبات بنجاح.
الأنواع
BackgroundSyncPlugin
صف ينفذ عملية معاودة الاتصال خلال دورة حياة fetchDidFail
يسهّل ذلك إضافة الطلبات التي تعذّر تنفيذها إلى قائمة انتظار المزامنة في الخلفية.
أماكن إقامة
-
الدالة الإنشائية
void
تبدو الدالة
constructor
على النحو التالي:(name: string, options?: QueueOptions) => {...}
-
اسم
سلسلة
راجِع مستندات
workbox-background-sync.Queue
لمعرفة تفاصيل المَعلمة. -
الخيارات
QueueOptions اختيارية
-
returns
-
Queue
فئة لإدارة تخزين الطلبات الفاشلة في IndexedDB وإعادة المحاولة لاحقًا. يمكن ملاحظة جميع أجزاء عملية التخزين وإعادة التشغيل عن طريق عمليات معاودة الاتصال.
أماكن إقامة
-
الدالة الإنشائية
void
تنشئ مثيلاً من قائمة الانتظار بالخيارات المحددة
تبدو الدالة
constructor
على النحو التالي:(name: string, options?: QueueOptions) => {...}
-
اسم
سلسلة
الاسم الفريد لقائمة المحتوى التالي. ويجب أن يكون هذا الاسم فريدًا لأنّه يُستخدم لتسجيل أحداث المزامنة وطلبات التخزين في IndexedDB الخاص بهذا المثيل. سيظهر خطأ إذا تم اكتشاف اسم مكرر.
-
الخيارات
QueueOptions اختيارية
-
returns
-
-
اسم
سلسلة
-
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
سلسلة
-
returns
-
-
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
.
-
returns
-
-
نسخة مماثلة
void
تنشئ هذه الدالة نسخة عميقة من المثيل وتعيدها.
تبدو الدالة
clone
على النحو التالي:() => {...}
-
returns
-
-
toObject
void
لعرض نسخة طبق الأصل من كائن
_requestData
في المثيلات.تبدو الدالة
toObject
على النحو التالي:() => {...}
-
returns
RequestData
-
-
toRequest
void
لتحويل هذا المثيل إلى طلب.
تبدو الدالة
toRequest
على النحو التالي:() => {...}
-
returns
الطلب
-
-
fromRequest
void
لتحويل كائن "طلب" إلى كائن عادي يمكن استنساخه أو ترميزه بسلسلة JSON.
تبدو الدالة
fromRequest
على النحو التالي:(request: Request) => {...}
-
طلب
الطلب
-
returns
Promise<StorableRequest>
-