استعمالات متقدمة للواجهة البرمجية Fetch في جافاسكربت
تُعتبر الواجهة البرمجية Fetch API من الأدوات الأساسية والمهمة في تطوير تطبيقات الويب الحديثة باستخدام جافاسكربت، حيث تسمح بإجراء طلبات HTTP إلى الخوادم والحصول على البيانات بشكل غير متزامن. رغم أن الاستخدام الأساسي لـ Fetch يتلخص في إرسال طلبات GET وPOST واستلام النتائج، إلا أن الإمكانات التي توفرها هذه الواجهة أعمق بكثير، وتسمح بتنفيذ عمليات متقدمة وفعالة تساعد على تحسين تجربة المستخدم وتطوير تطبيقات أكثر تعقيدًا.
في هذا المقال، سيتم استعراض واستكشاف استعمالات متقدمة للواجهة البرمجية Fetch، متضمنة معالجة الأخطاء المعقدة، استخدام الخيارات المتقدمة للطلب، التعامل مع البيانات الثنائية والملفات، استخدام خواص التزامن المتقدمة، إدارة الوقت، والتكامل مع تقنيات أخرى مثل Service Workers وStreams وغيرها.
1. مراجعة أساسية للواجهة البرمجية Fetch
قبل الخوض في التفاصيل المتقدمة، من الضروري فهم الأساسيات التي تقوم عليها Fetch. تعتمد Fetch على Promises، حيث تُعيد دالة fetch() Promise تُحل عند اكتمال الطلب أو تُرفض في حال حدوث خطأ.
jsfetch('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 لإرسال معلومات إضافية مع الطلب مثل نوع المحتوى، التوثيق، أو إعدادات التحكم في التخزين المؤقت.
jsfetch('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 لتحديد كيفية التعامل مع سياسة الأمان.
jsfetch('https://cross-origin-site.com/data', {
mode: 'cors', // يسمح بالطلبات عبر المصادر
credentials: 'include' // إرسال الكوكيز مع الطلب
});
2.3 إرسال بيانات POST وPUT متقدمة
إرسال بيانات JSON أو بيانات نموذجية FormData عبر Fetch يحتاج لضبط الرأس المناسب والجسم:
jsfetch('https://api.example.com/update', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({name: 'John', age: 30})
});
أو إرسال ملفات عبر FormData:
jsconst 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، بل يجب التحقق يدويًا:
jsfetch('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 التي تنفذ سواء نجح الطلب أو فشل، مما يمكن استخدامه في تنظيف واجهة المستخدم مثل إخفاء مؤشر تحميل.
jsfetch('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 قراءة الاستجابة تدريجياً
jsfetch('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 الذي يسمح بإلغاء الطلب بعد فترة محددة.
jsconst 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
jsself.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 مع دعم خاصية إرسال بيانات متعددة الأجزاء.
jsconst 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 مثال على تحميل صورة ومعالجتها
jsfetch('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 لضمان إتمام الطلب حتى لو أغلق المستخدم الصفحة.
jsnavigator.sendBeacon('https://api.example.com/log', JSON.stringify({event: 'page_close'}));
رغم أن sendBeacon ليست جزءًا من Fetch API، إلا أنها مكملة له في التعامل مع طلبات الإرسال غير المتزامنة عند إغلاق الصفحة.
10. استخدام Fetch مع Async/Await لصياغة أكثر نظافة وقابلية للصيانة
استخدام async/await يجعل الكود أكثر وضوحًا وسهل القراءة مقارنة باستخدام then/catch، خاصة مع العمليات المعقدة.
jsasync 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 بشكل متقن تمتد لتشمل تحسين الأمن، الأداء، وتجربة المستخدم، مما يجعل تعلم التقنيات المتقدمة المرتبطة بها ضرورة لكل مطور جافاسكربت يسعى لإتقان تطوير الويب الحديث.
المصادر والمراجع
-
MDN Web Docs – Using Fetch: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
-
MDN Web Docs – Streams API: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API
-
WHATWG Fetch Standard: https://fetch.spec.whatwg.org/

