البرمجة

استعمالات متقدمة لـ Fetch

جدول المحتوى

استعمالات متقدمة للواجهة البرمجية Fetch في جافاسكربت

تُعتبر الواجهة البرمجية Fetch API من الأدوات الأساسية والمهمة في تطوير تطبيقات الويب الحديثة باستخدام جافاسكربت، حيث تسمح بإجراء طلبات HTTP إلى الخوادم والحصول على البيانات بشكل غير متزامن. رغم أن الاستخدام الأساسي لـ Fetch يتلخص في إرسال طلبات GET وPOST واستلام النتائج، إلا أن الإمكانات التي توفرها هذه الواجهة أعمق بكثير، وتسمح بتنفيذ عمليات متقدمة وفعالة تساعد على تحسين تجربة المستخدم وتطوير تطبيقات أكثر تعقيدًا.

في هذا المقال، سيتم استعراض واستكشاف استعمالات متقدمة للواجهة البرمجية Fetch، متضمنة معالجة الأخطاء المعقدة، استخدام الخيارات المتقدمة للطلب، التعامل مع البيانات الثنائية والملفات، استخدام خواص التزامن المتقدمة، إدارة الوقت، والتكامل مع تقنيات أخرى مثل Service Workers وStreams وغيرها.


1. مراجعة أساسية للواجهة البرمجية Fetch

قبل الخوض في التفاصيل المتقدمة، من الضروري فهم الأساسيات التي تقوم عليها Fetch. تعتمد Fetch على Promises، حيث تُعيد دالة fetch() Promise تُحل عند اكتمال الطلب أو تُرفض في حال حدوث خطأ.

js
fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));

هذه الطريقة البسيطة تستخدم لاسترجاع بيانات JSON من API، لكنها تمثل فقط البداية لما يمكن تنفيذه.


2. استخدام الخيارات المتقدمة للطلب (Request Options)

تسمح Fetch API بتمرير كائن إعدادات يحتوي على خصائص متقدمة للتحكم في نوع الطلب HTTP (GET, POST, PUT, DELETE، الخ)، ترويسات (headers)، وضع الـ mode، التحكم في الكوكيز، وغيرها.

2.1 إعداد ترويسات متقدمة (Headers)

يمكن استخدام ترويسات HTTP لإرسال معلومات إضافية مع الطلب مثل نوع المحتوى، التوثيق، أو إعدادات التحكم في التخزين المؤقت.

js
fetch('https://api.example.com/secure-data', { method: 'GET', headers: { 'Authorization': 'Bearer token123', 'Accept': 'application/json', 'Cache-Control': 'no-cache' } }) .then(response => response.json()) .then(data => console.log(data));

2.2 التحكم في وضع CORS (Cross-Origin Resource Sharing)

عند العمل مع مصادر خارجية مختلفة (Cross-Origin)، يمكن تحديد الوضع عبر خاصية mode لتحديد كيفية التعامل مع سياسة الأمان.

js
fetch('https://cross-origin-site.com/data', { mode: 'cors', // يسمح بالطلبات عبر المصادر credentials: 'include' // إرسال الكوكيز مع الطلب });

2.3 إرسال بيانات POST وPUT متقدمة

إرسال بيانات JSON أو بيانات نموذجية FormData عبر Fetch يحتاج لضبط الرأس المناسب والجسم:

js
fetch('https://api.example.com/update', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({name: 'John', age: 30}) });

أو إرسال ملفات عبر FormData:

js
const formData = new FormData(); formData.append('file', fileInput.files[0]); fetch('https://api.example.com/upload', { method: 'POST', body: formData });

3. التعامل مع الأخطاء وتحسين استجابة التطبيق

3.1 التحقق من استجابة HTTP

بشكل افتراضي، Fetch لا يعتبر أي استجابة HTTP غير ناجحة كخطأ في الـ Promise، بل يجب التحقق يدويًا:

js
fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => console.log(data)) .catch(error => console.error('Fetch error:', error));

3.2 التعامل مع أخطاء الشبكة

أخطاء الشبكة مثل فقد الاتصال أو انتهاء المهلة تؤدي إلى رفض الـ Promise ويمكن التعامل معها عبر .catch.

3.3 استخدام خاصية finally

تسمح Promise بخاصية finally التي تنفذ سواء نجح الطلب أو فشل، مما يمكن استخدامه في تنظيف واجهة المستخدم مثل إخفاء مؤشر تحميل.

js
fetch('https://api.example.com/data') .then(response => response.json()) .then(data => processData(data)) .catch(error => handleError(error)) .finally(() => hideLoadingIndicator());

4. استخدام Fetch مع Streams لقراءة البيانات بشكل متقدم

تدعم Fetch قراءة الاستجابة كـ Streams مما يسمح بمعالجة البيانات تدريجياً، مثل تنزيل ملفات كبيرة أو التعامل مع محتوى يتم توليده أثناء الإرسال (مثل فيديوهات أو بيانات صوتية).

4.1 قراءة الاستجابة تدريجياً

js
fetch('https://api.example.com/largefile') .then(response => { const reader = response.body.getReader(); let receivedLength = 0; const chunks = []; function read() { return reader.read().then(({done, value}) => { if (done) { const blob = new Blob(chunks); // التعامل مع الملف كاملاً return blob; } chunks.push(value); receivedLength += value.length; return read(); }); } return read(); }) .then(blob => { // معالجة الملف أو عرضه });

هذه التقنية تسمح بتنزيل البيانات بكفاءة عالية دون الحاجة لتحميل الملف كاملاً دفعة واحدة.


5. التحكم في مهلة الطلب (Timeout)

Fetch API لا تدعم مهلة طلب مدمجة، لكن يمكن تنفيذ مهلة يدويًا باستخدام AbortController الذي يسمح بإلغاء الطلب بعد فترة محددة.

js
const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); // إلغاء الطلب بعد 5 ثواني fetch('https://api.example.com/data', { signal: controller.signal }) .then(response => response.json()) .then(data => { clearTimeout(timeoutId); console.log(data); }) .catch(error => { if (error.name === 'AbortError') { console.error('Request timed out'); } else { console.error('Fetch error:', error); } });

6. التفاعل مع Service Workers لتحسين تجربة المستخدم

يمكن استخدام Fetch داخل Service Workers للتحكم في كيفية التعامل مع الطلبات والاستجابات، مما يسمح ببناء تطبيقات ويب تقدم تجربة استخدام خارج الإنترنت (Offline First) وتحسين سرعة التحميل.

6.1 التقاط طلبات Fetch داخل Service Worker

js
self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request).then(cachedResponse => { if (cachedResponse) { return cachedResponse; } return fetch(event.request).then(response => { return caches.open('dynamic-cache').then(cache => { cache.put(event.request, response.clone()); return response; }); }); }) ); });

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


7. إرسال بيانات متعددة الأجزاء (Multipart) مع Fetch

لرفع ملفات أو إرسال بيانات معقدة يمكن استخدام FormData مع دعم خاصية إرسال بيانات متعددة الأجزاء.

js
const formData = new FormData(); formData.append('username', 'user123'); formData.append('avatar', fileInput.files[0]); fetch('https://api.example.com/profile', { method: 'POST', body: formData });

يُستخدم هذا في رفع الصور، المستندات، أو أي ملفات أخرى، مع إمكانية دمجها مع بيانات نصية.


8. التعامل مع محتوى ثنائي (Binary Data)

في بعض الحالات يحتاج المطور إلى التعامل مع محتوى ثنائي مثل الصور، ملفات الصوت، أو ملفات PDF. يوفر Fetch طرقًا مختلفة لتحويل الاستجابة:

  • response.arrayBuffer() لقراءة البيانات كـ ArrayBuffer.

  • response.blob() لتحويل البيانات إلى Blob.

  • response.formData() لقراءة بيانات النموذج.

8.1 مثال على تحميل صورة ومعالجتها

js
fetch('https://api.example.com/image.jpg') .then(response => response.blob()) .then(blob => { const imgURL = URL.createObjectURL(blob); document.querySelector('img').src = imgURL; });

9. تحسين الأداء باستخدام keepalive وkeepalive في الطلبات

في حالات معينة مثل إرسال طلبات أثناء إغلاق الصفحة (onunload event)، يمكن استخدام خيار keepalive لضمان إتمام الطلب حتى لو أغلق المستخدم الصفحة.

js
navigator.sendBeacon('https://api.example.com/log', JSON.stringify({event: 'page_close'}));

رغم أن sendBeacon ليست جزءًا من Fetch API، إلا أنها مكملة له في التعامل مع طلبات الإرسال غير المتزامنة عند إغلاق الصفحة.


10. استخدام Fetch مع Async/Await لصياغة أكثر نظافة وقابلية للصيانة

استخدام async/await يجعل الكود أكثر وضوحًا وسهل القراءة مقارنة باستخدام then/catch، خاصة مع العمليات المعقدة.

js
async function fetchData() { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error('Fetch error:', error); } }

11. مقارنة بين Fetch وXMLHttpRequest في الاستخدام المتقدم

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

لكن Fetch يتفوق في التعامل مع Promises، كتابة كود نظيف، ودعم API متقدمة مثل Streams، AbortController، و Service Workers.


12. جدول ملخص لاستعمالات Fetch المتقدمة

الاستخدام الوصف مثال/خاصية مرتبطة
ترويسات متقدمة (Headers) تخصيص ترويسات HTTP للطلب headers
التحكم في وضع CORS تحديد سياسة المشاركة عبر المصادر mode, credentials
إرسال بيانات POST/PUT إرسال JSON، FormData، ملفات body, Content-Type
التعامل مع الأخطاء التحقق من حالة الاستجابة والتعامل مع الأخطاء response.ok, catch
قراءة البيانات عبر Streams تحميل ملفات كبيرة أو بيانات متدفقة تدريجياً response.body.getReader()
التحكم في مهلة الطلب إلغاء الطلب بعد فترة زمنية محددة AbortController
تكامل مع Service Workers إدارة التخزين المؤقت والتحكم بالطلبات self.addEventListener('fetch')
إرسال بيانات متعددة الأجزاء رفع ملفات وبيانات مركبة FormData
التعامل مع المحتوى الثنائي تحميل وعرض صور، ملفات PDF، صوتيات response.blob(), arrayBuffer()
تحسين الأداء بإرسال الطلبات عند إغلاق الصفحة استخدام keepalive أو sendBeacon لإرسال بيانات قبل مغادرة الصفحة keepalive, sendBeacon

خاتمة

تمثل Fetch API حجر الزاوية في التعامل مع البيانات والخدمات على الويب باستخدام جافاسكربت، وتجاوز الاستخدام البسيط للواجهة البرمجية يمكن أن يفتح آفاقًا واسعة لتحسين أداء التطبيقات، زيادة كفاءتها، وضمان تجربة مستخدم أفضل. من خلال التحكم المتقدم في الترويسات، إدارة الأخطاء بطرق دقيقة، التعامل مع البيانات الثنائية، والاستفادة من إمكانيات Streams وService Workers، يمكن للمطورين بناء تطبيقات ويب قوية وحديثة تلبي متطلبات العصر.

كما أن دمج Fetch مع أساليب التزامن الحديثة مثل async/await يجعل كتابة الكود أكثر نظافة وسهولة في الصيانة. ومع توفر أدوات مثل AbortController وsendBeacon، يمكن التحكم في الطلبات بشكل أفضل لضمان الاستجابة المناسبة حتى في ظروف غير متوقعة مثل انقطاع الاتصال أو إغلاق الصفحة.

أهمية استخدام Fetch بشكل متقن تمتد لتشمل تحسين الأمن، الأداء، وتجربة المستخدم، مما يجعل تعلم التقنيات المتقدمة المرتبطة بها ضرورة لكل مطور جافاسكربت يسعى لإتقان تطوير الويب الحديث.


المصادر والمراجع