Fetch API
ما هو Fetch API
Fetch API هو أحدث آلية تم إضافتها في جافاسكربت لجعل صفحات الويب قادرة على إرسال طلبات المستخدم ( User Requests ) و استقبال ردود السيرفر ( Server Responses ) بشكل متزامن و بدون الحاجة إلى تحديث الصفحة أو الإنتقال لصفحة جديدة.
إرسال الطلبات بشكل متزامن إلى السيرفر تحتاجه في حالات كثيرة نذكر منها:
- لجعل المستخدم قادر على إضافة تعليق جديد.
- لجعل المستخدم قادر على جلب التعليقات و عرضها له.
- لجعل المستخدم قادر على تقييم محتوى الصفحة.
- لجعل المستخدم قادر على رفع صورة و ضبط حجمها إلخ..
في هذا الدرس ستتعلم كيفية إرسال طلبات متزامنة إلى السيرفر عن طريق Fetch API خطوة خطوة.
قبل وجود Fetch API كانت طريقة إرسال و استقبال البيانات بشكل متزامن تتم من خلال استخدام آلية XMLHttpRequest و التي كان استخدامها أصعب و غالباً ما كان مطوروا الويب يلجؤوا الى استخدام مكتبة JQuery التي كانت تجعل هذا الأمر سهل للغاية.
كيفية استخدام Fetch API
بدايةً، عليك معرفة أن Fetch API مبني بأسلوب البرومس ( Promise ) مما يعني أنه عند إرسال الطلبات بواسطته فإنه يتم إرسالها بشكل متزامن و النتيجة التي ترجع قد تكون نتيجة صحيحة و قد تكون نتيجة مرفوضة بالإضافة إلى أنه قد يحدث مشكلة أثناء التنفيذ.
التعامل مع Fetch API يتم من خلال الدالة
fetch(resource, options)
- مكان البارميتر
resource نمرر المصدر أو الرابط الذي سيتم إرسال الطلب إليه. options هو باراميتر إختياري - على حسب نوع الطلب - و مكانه يمكن تمرير كائن فيه معلومات مرتبطة بالطلب.
بما أن الدالة
سنشرح كلا الأسلوبين و لكننا سنركز على أسلوب
كمثال بسيط، هكذا يكون شكل الطلب الذي يعطينا الصفحة الرئيسية في موقع هرمش.
المثال الأول
fetch("https://codafox.blogspot.com/")
تحديد طريقة إرسال الطلب في Fetch API
كل طلب يتم إرساله إلى السيرفر يتم إرساله بطريقة محددة (
الطريقة | إستعمالها |
---|---|
هذه الطريقة الإفتراضية في إرسال الطلبات و هي تستعمل لإرسال طلب عام، مثل طلب إحضار صفحة من الموقع. | |
هذا الطريقة تستعمل لإرسال طلب خاص، مثل طلب تسجيل دخول في الموقع. | |
هذا الطريقة تستعمل لإرسال طلب لتعديل كلي، مثل طلب تعديل كل معلومات المستخدم. | |
هذا الطريقة تستعمل لإرسال طلب تعديل جزئي، مثل طلب تعديل بعض معلومات المستخدم. | |
هذا الطريقة تستعمل لإرسال طلب للحذف، مثل طلب لحذف مستخدم. |
على الرغم من أن طرق إرسال الطلبات تحدد ما يقوم به الطلب بدقة إلا أنه عادةً ما يستعمل المطورون الطريقة
فعلياً، هكذا يكون شكل الطلب الذي يعطينا الصفحة الرئيسية في موقع هرمش.
المثال الأول
fetch("https://codafox.blogspot.com/", {
method: 'GET',
})
و هكذا يكون شكل الطلب الذي يسمح لنا بحذف مقال من موقع هرمش.
المثال الثاني
fetch("https://codafox.blogspot.com/posts/1", {
method: 'DELETE',
})
تحديد نوع الطلب أو الرد في Fetch API
الطلبات التي يتم إرسالها للسيرفر أو الردود التي تأتي منه يجب أن يتم تحديد أنواعها (
الجدول التالي يتضمن أنواع الطلبات و الردود التي يجب معرفتها.
نوع الرد | معناه |
---|---|
هذا هو نوع الرد الإفتراضي و معناه أن الرد سيتم إستلامه على شكل نص عادي. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل نص و لكنه بلغة HTML. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل نص و لكنه بلغة CSS. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل نص و لكنه بلغة XML. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل نص و لكنه بلغة CSV. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل نص و لكنه بلغة JavaScript. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل نص و لكنه بأسلوب JSON. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل صورة نوعها PNG. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل صورة نوعها APNG. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل صورة نوعها GIF. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل صورة نوعها JPEG. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل صورة نوعها SVG. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل صورة نوعها WEBP. | |
هذا النوع من الرد معناه أن الرد سيتم إستلامه على شكل Binary و هذا النوع لا يمكن عرضه في الصفحة، مما يجعل المستخدم قادر على حفظ هذا الرد كملف على جهازه. | |
هذا النوع يستعمل عند إرسال بيانات النموذج و التي قد تتضمن ملف أو صورة مرفقة معها. |
كمثال بسيط، هكذا يكون شكل الطلب الذي يسمح لنا بتسجيل الدخول في موقع هرمش.
لاحظ أن الطريقة التي استخدمناه في إرسال الطلب للسرفر هي
مثال
fetch("https://codafox.blogspot.com/account/login", {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({
username: 'test',
password: '1234',
}
})
أساليب عرض نتائج طلبات Fetch API
في المثال التالي قمنا بجلب ملف نصي إسمه
مثال
<!DOCTYPE html>
<html>
<body>
<script>
// وضعنا فيه مسار الملف الذي سنقوم بطلبه url المتغير
let url = 'https://codafox.blogspot.com/tutorials/javascript/fetch-api/demo.txt';
// هنا قمنا بإرسال طلب لإحضار الملف و من ثم عرض نتيجته في الصفحة
fetch(url)
.then(response => response.text())
.then(data => document.write(data));
// كل المحتوى الذي سيظهر في الصفحة موجود في الأصل في الملف الذي قمنا بطلبه
</script>
</body>
</html>
المثال السابق نفسه يمكن كتابته بأسلوب
مثال
<!DOCTYPE html>
<html>
<body>
<script>
// fetchData كود جلب إرسال الطلب وضعناه كله بداخل دالة إسمها
async function fetchData() {
// وضعنا فيه مسار الملف الذي سنقوم بطلبه url المتغير
let url = 'https://codafox.blogspot.com/tutorials/javascript/fetch-api/demo.txt';
// response هنا قمنا بإرسال طلب لإحضار الملف مع تخزين النتيجة في المتغير
let response = await fetch(url);
// data إلى نص و تخزينها في المتغير response هنا قمنا بتحويل النتيجة الموجودة في المتغير
let data = await response.text();
// في الصفحة data هنا قمنا بعرض النص الموجود في المتغير
document.write(data);
}
// حتى تتنفذ fetchData() هنا قمنا باستدعاء الدالة
fetchData();
</script>
</body>
</html>
معالجة الأخطاء في Fetch API
عند طلب بيانات من أي سيرفر فإنه قد يعرض لك نتيجة مرفوضة ( Rejected ) أو قد يحدث خطأ ( Error ) ما أثناء تنفيذ الطلب.
نتيجة الطلب تعتبر مقبولة ( Resolved ) إذا كانت حالة التنفيذ المرفقة في رد السيرفر هي
غير ذلك تعتبر فإن نتيجة تعتبر غير مقبولة و هنا فإننا في الواقع نظهرها كخطأ للمستخدم.
إذاً بناءاُ على رد السيرفر يمكنك معرفة ما إن كانت القيمة مقبولة أم لا.
في الأمثلة التالية، مسار الملف الذي سنحاول جلبه كتبناه بشكل خاطئ عمداً حتى نتسبب بخطأ.
عند تجربة الأمثلة فإنه عليك الإنتظار لبضعة ثواني ريثما يظهر لك أنه يوجد خطأ.
إذا كنت تستخدم الدالة
مثال
<!DOCTYPE html>
<html>
<body>
<script>
// وضعنا فيه مسار الملف الذي سنقوم بطلبه و الذي نعلم أنه مكتوب بشكل خاطئ url المتغير
let url = 'https://wrong-path-name/demo.txt';
// هنا قمنا بإرسال طلب لإحضار الملف و من ثم عرض نتيجته في الصفحة
fetch(url)
.then(response => {
// هنا قمنا بفحص الرد الذي أتى من السيرفر لمعرفة ما إن كان قد رد بقيمة مقبولة أم لا
if (!response.ok) {
// إذا كان قد رد بقيمة غير مقبولة، سيتم عرض النص التالي كخطأ وقع عند التنفيذ
throw new Error('Response was not ok');
}
// إذا كان الرد مقبولاً فسيتم تحويله إلى نص ليتم عرضه من بعدها
return response.text()
)
.then(data => document.write(data))
.catch(error => document.write(error));
</script>
</body>
</html>
إذا كنت تستخدم
مثال
<!DOCTYPE html>
<html>
<body>
<script>
async function fetchData() {
try {
// وضعنا فيه مسار الملف الذي سنقوم بطلبه و الذي نعلم أنه مكتوب بشكل خاطئ url المتغير
let url = 'https://wrong-path-name/demo.txt';
// response هنا قمنا بإرسال طلب لإحضار الملف مع تخزين النتيجة في المتغير
let response = await fetch(url);
// هنا قمنا بفحص الرد الذي أتى من السيرفر لمعرفة ما إن كان قد رد بقيمة مقبولة أم لا
if (!response.ok) {
// إذا كان قد رد بقيمة غير مقبولة، سيتم عرض النص التالي كخطأ وقع عند التنفيذ
throw new Error('Response was not ok');
}
// data إلى نص و تخزينها في المتغير response هنا قمنا بتحويل النتيجة الموجودة في المتغير
let data = await response.text();
// في الصفحة data هنا قمنا بعرض النص الموجود في المتغير
document.write(data);
}
catch (error) {
// إذا حصل أي خطأ، سيتم طباعته في الصفحة
document.write(error);
}
}
// حتى تتنفذ fetchData() هنا قمنا باستدعاء الدالة
fetchData();
</script>
</body>
</html>
أمثلة شاملة على استخدام Fetch API
سنطبق جميع الأمثلة التالية على API وهمي مجاني تابع لموقع typicode-a.com.
في المثال التالي قمنا بطلب مقال واحد من الموقع و من ثم قمنا بعرض محتواه في الصفحة.
المثال الأول
<!DOCTYPE html>
<html>
<body>
<script>
// هنا قمنا بتعريف دالة تقوم بإرسال طلب إلى السيرفر بشكل متزامن و من ثم تعرض نتيجته
async function fetchData() {
// وضعنا فيه مسار الموقع الذي سنقوم بإرسال الطلب إليه url المتغير
let url = 'https://jsonplaceholder.typicode.com/posts/1';
try {
// url هنا قمنا بإرسال طلب إلى الرابط الموجود في
let response = await fetch(url);
// هنا قمنا بفحص الرد الذي أتى من السيرفر لمعرفة ما إن كان قد تم العثور على المقال بنجاح أم لا
if (!response.ok) {
// إذا كان رد السيرفر يفيد بأن قد فشل في جلبه فسيتم
// عرض النص التالي للإشارة إلى وقوع خطأ عند التنفيذ
throw new Error('Network response was not ok');
}
// إلى كائن جافاسكربت JSON إذا كان الرد مقبولاً فسيتم تحويله من صيغة
let data = await response.json();
// بعد أن تم تحويل الرد إلى كائن، سيتم عرض القيم الموجودة فيه
document.write(`
<p>Fetched data:</p>
<ul>
<li>userId: ${data.userId}</li>
<li>id: ${data.id}</li>
<li>title: ${data.title}</li>
<li>body: ${data.body}</li>
</ul>`);
}
catch(error) {
// إذا حصل أي خطأ أثناء جلب البيانات سيتم عرضه
alert(error);
}
}
// حتى تتنفذ fetchData() هنا قمنا باستدعاء الدالة
fetchData();
</script>
</body>
</html>
قمنا بكتابة
في المثال التالي قمنا بطلب جميع المقالات من الموقع و من ثم قمنا بعرض محتواه في الصفحة.
ملاحظة: سيتم إحضار مئة مقال، سنعرض من كل مقال عنوانه و الفقرة الموجودة فيه.
المثال الثاني
<!DOCTYPE html>
<html>
<body>
<script>
// هنا قمنا بتعريف دالة تقوم بإرسال طلب إلى السيرفر بشكل متزامن و من ثم تعرض نتيجته
async function fetchData() {
// وضعنا فيه مسار الموقع الذي سنقوم بإرسال الطلب إليه url المتغير
let url = 'https://jsonplaceholder.typicode.com/posts';
try {
// url هنا قمنا بإرسال طلب إلى الرابط الموجود في
let response = await fetch(url);
// هنا قمنا بفحص الرد الذي أتى من السيرفر لمعرفة ما إن كان قد تم العثور على المقالات بنجاح أم لا
if (!response.ok) {
// إذا كان رد السيرفر يفيد بأن قد فشل في جلبهم فسيتم
// عرض النص التالي للإشارة إلى وقوع خطأ عند التنفيذ
throw new Error('Network response was not ok');
}
// إلى مصفوفات كائنات جافاسكربت JSON إذا كان الرد مقبولاً فسيتم تحويله من صيغة
let data = await response.json();
// بعد أن تم تحويل الرد إلى مصفوفة كائنات، سيتم المرور عليهم جميعاً و عرض بعض قيمهم
for(x of data) {
document.write(`
<h3>${x.title}</h3>
<p>${x.body}</p>
<hr>`);
}
}
catch(error) {
// إذا حصل أي خطأ أثناء جلب البيانات سيتم عرضه
alert(error);
}
}
// حتى تتنفذ fetchData() هنا قمنا باستدعاء الدالة
fetchData();
</script>
في المثال التالي قمنا بإرسال طلب لإنشاء مقال جديد في الموقع.
المتعارف عليه إذا تم إنشاء المقال بنجاح، هو أن يقوم السيرفر بإرجاع نسخة منه.
ملاحظة: قمنا إرسال بيانات المقال على شكل JSON للسيرفر.
المثال الثالث
<!DOCTYPE html>
<html>
<body>
<script>
// هنا قمنا بتعريف دالة تقوم بإرسال طلب إلى السيرفر بشكل متزامن و من ثم تعرض نتيجته
async function fetchData() {
// وضعنا فيه مسار الموقع الذي سنقوم بإرسال الطلب إليه url المتغير
let url = 'https://jsonplaceholder.typicode.com/posts';
try {
// url هنا قمنا بإرسال طلب إلى الرابط الموجود في
// POST مع تحديد طريقة إرسال الطلب على أنها
// JSON و أن البيانات المرسلة في الطلب موضوعة بأسلوب
let response = await fetch(url, {
method: 'POST',
body: JSON.stringify({
title: 'This is the title',
body: 'This is is the content',
userId: 1
}),
headers: {
'Content-type': 'application/json; charset=UTF-8'
}
});
// هنا قمنا بفحص الرد الذي أتى من السيرفر لمعرفة ما إن كان قد تم إنشاء المقال أم لا
if (!response.ok) {
// إذا حدثت أي مشكلة عند محاولة الإتصال بالموقع لإنشاء المقال، سيتم عرض النص التالي كخطأ
throw new Error('Network response was not ok');
}
// إذا تم إنشاء المقال بنجاح سيقوم السيرفر بإرجاع نسخة منه
// و لكننا سنقوم بتحويلها إلى كائن جافاسكربت JSON على هيئة
let data = await response.json();
// بعد أن تم تحويل رد السيرفر إلى كائن، سيتم عرض القيم الموجودة فيه
document.write(`
<p>The newely created post data:</p>
<ul>
<li>userId: ${data.userId}</li>
<li>id: ${data.id}</li>
<li>title: ${data.title}</li>
<li>body: ${data.body}</li>
</ul>`);
}
catch(error) {
// إذا حصل أي خطأ أثناء جلب البيانات سيتم عرضه
alert(error);
}
}
// حتى تتنفذ fetchData() هنا قمنا باستدعاء الدالة
fetchData();
</script>
</body>
</html>
في المثال التالي قمنا بحذف مقال واحد من الموقع و من بعدها قمنا بإظهار رسالة تفيد بأنه تم حذفه.
المثال الرابع
<!DOCTYPE html>
<html>
<body>
<script>
// هنا قمنا بتعريف دالة تقوم بإرسال طلب إلى السيرفر بشكل متزامن و من ثم تعرض نتيجته
async function fetchData() {
// وضعنا فيه مسار الموقع الذي سنقوم بإرسال الطلب إليه url المتغير
let url = 'https://jsonplaceholder.typicode.com/posts/1';
try {
// url هنا قمنا بإرسال طلب إلى الرابط الموجود في
let response = await fetch(url, {
method: 'DELETE'
});
// هنا قمنا بفحص الرد الذي أتى من السيرفر لمعرفة ما إن كان قد تم الحذف بنجاح أم لا
if (!response.ok) {
// إذا كان رد السيرفر يفيد بأن الحذف قد فشل فسيتم
// عرض النص التالي للإشارة إلى وقوع خطأ عند التنفيذ
throw new Error('Network response was not ok');
}
// إذا لم يقع أي خطأ فهذا يعني أنه تم الحذف بنجاح و سيتم إعلام المستخدم بذلك
document.write(`<p>The post is deleted successfuly.</p>`);
}
catch(error) {
// إذا حصل أي خطأ أثناء تنفيذ أو إرسال الطلب سيتم عرضه
alert(error);
}
}
// حتى تتنفذ fetchData() هنا قمنا باستدعاء الدالة
fetchData();
</script>
</body>
</html>