أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
البرمجة بالعربي

AbortController: ألغِ طلبات fetch القديمة قبل ما تسبقك

📅 ٢٦ أبريل ٢٠٢٦⏱ 4 دقائق قراءة
AbortController: ألغِ طلبات fetch القديمة قبل ما تسبقك

AbortController: ألغِ طلبات fetch القديمة قبل ما تسبقك

مستوى القارئ: متوسط

هتمنع واجهة البحث من عرض نتائج قديمة، وهتقلل الضغط على الـ API، بدل ما كل حرف يفتح طلب جديد يفضل شغال لحد ما يرجع.

المشكلة باختصار

ركز في سيناريو autocomplete بسيط. المستخدم كتب s ثم se ثم sea بسرعة. الواجهة أرسلت 3 طلبات. الطبيعي إن آخر طلب هو المهم، لكن اللي بيحصل فعلاً إن الطلب الأول ممكن يرجع بعد الثالث بسبب الشبكة أو الكاش أو ضغط السيرفر. النتيجة: المستخدم كتب كلمة أحدث، والواجهة تعرض نتيجة أقدم.

الطريقة الشائعة الغلط هي الاعتماد على debounce فقط. الـ debounce مفيد، لكنه لا يلغي طلبًا خرج بالفعل. لو الطلب وصل للسيرفر، سيظل يستهلك وقتًا وذاكرة واتصالًا. الحل هنا هو AbortController: إشارة إلغاء تمررها إلى fetch، ثم تستدعي abort() قبل إرسال طلب جديد.

مخطط يوضح إلغاء طلبات fetch القديمة باستخدام AbortController قبل إرسال طلب بحث جديد

مثال بسيط قبل التعريف العلمي

اعتبر إن عندك موظف دعم يرد على آخر رسالة من العميل. العميل بعت 8 رسائل وراء بعض، والمهم هو آخر رسالة فقط. لو الموظف فضل يرد على أول رسالة، الرد هيبقى صحيح لغويًا لكنه غير مناسب للحالة الحالية. AbortController يعمل نفس الفكرة للطلبات غير المهمة: أول ما طلب أحدث يظهر، الطلب القديم يأخذ إشارة توقف.

علميًا، AbortController كائن يولد AbortSignal. هذه الإشارة تمر إلى واجهات غير متزامنة مثل fetch. عند استدعاء abort()، يتم رفض وعد fetch عادة بخطأ اسمه AbortError، وتقدر تتجاهله لأنه إلغاء مقصود وليس فشلًا حقيقيًا.

الحل العملي في JavaScript

الافتراض إن عندك input بحث يضرب endpoint مثل /api/products?q=.... الهدف: طلب واحد نشط فقط لكل مستخدم في نفس خانة البحث.

JavaScript
let searchController = null;

async function searchProducts(query) {
  if (searchController) {
    searchController.abort();
  }

  searchController = new AbortController();

  try {
    const response = await fetch(`/api/products?q=${encodeURIComponent(query)}`, {
      signal: searchController.signal,
    });

    if (!response.ok) throw new Error(`HTTP ${response.status}`);

    const products = await response.json();
    renderProducts(products);
  } catch (error) {
    if (error.name === 'AbortError') return;
    showSearchError('البحث فشل. جرّب مرة تانية.');
  }
}

const input = document.querySelector('#product-search');
input.addEventListener('input', (event) => {
  const query = event.target.value.trim();
  if (query.length < 2) return;
  searchProducts(query);
});

لو المستخدم كتب 8 حروف بسرعة، النسخة القديمة قد تترك 8 طلبات نشطة أو معلقة. النسخة دي تجعل الطلبات القديمة ملغاة، ويبقى آخر طلب فقط هو المرشح لتحديث الواجهة. الرقم هنا تقديري مبني على إدخال سريع بدون debounce؛ في تطبيق حقيقي ستقيسه من Network tab أو من عداد active requests في السيرفر.

رسم أعمدة يقارن عدد طلبات البحث النشطة قبل وبعد استخدام AbortController

أضف timeout بدل الانتظار المفتوح

في Node.js الحديث، تقدر تستخدم AbortSignal.timeout(ms) مع بعض APIs المعتمدة على الوعود. في المتصفح، تقدر تعمل timeout يدوي. أفضل طريقة في الواجهات الحساسة هي الجمع بين إلغاء الطلب السابق وحد أقصى للانتظار.

JavaScript
async function fetchWithTimeout(url, ms = 3000) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), ms);

  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } finally {
    clearTimeout(timer);
  }
}

لو الـ API طبيعيًا يرد خلال 200 إلى 600ms، timeout بقيمة 3000ms معقول. لو عندك بحث بطيء مقصود في أرشيف كبير، 3 ثواني قد تكون عدوانية. الـ trade-off هنا واضح: تكسب واجهة أسرع وطلبات أقل معلقة، لكن ممكن تقطع طلبًا كان سيعود بنتيجة صحيحة بعد زمن أطول.

ما الذي لا يحله AbortController؟

  • لا يضمن أن السيرفر أوقف العمل الداخلي فورًا. هو يلغي الطلب من جهة العميل، لكن السيرفر قد يكون بدأ تنفيذ query بالفعل.
  • لا يغني عن debounce. استخدم الاثنين معًا في autocomplete: debounce يقلل عدد الطلبات قبل خروجها، وAbortController يلغي ما خرج وصار قديمًا.
  • لا يصلح كحل وحيد لمشاكل backend بطيء. لو query نفسها تأخذ 4 ثواني، أصلح الفهرسة أو الكاش قبل تحميل الواجهة مسؤولية المشكلة.

متى لا تستخدم هذه الطريقة

لا تستخدمها مع عمليات لازم تكتمل حتى لو المستخدم غيّر الشاشة، مثل حفظ طلب دفع أو إرسال نموذج مهم. في هذه الحالات الإلغاء من الواجهة قد يربك المستخدم، والأفضل تصميم idempotency واضح في السيرفر. كذلك لا تستخدمها لو كل طلب يمثل عملية audit أو logging يجب تسجيلها كاملة.

مصادر اعتمدت عليها

  • MDN: AbortController abort()
  • MDN: AbortSignal واستخدامه مع fetch
  • Node.js Docs: AbortController وAbortSignal.timeout

الخطوة التالية

افتح أكثر input عندك يرسل طلبات متكررة، واضف عدادًا بسيطًا للطلبات النشطة قبل وبعد AbortController. لو الرقم لا ينزل من أكثر من 3 طلبات إلى طلب واحد غالبًا، راجع مكان حفظ controller ولا تنشئه داخل scope يضيع بين كل input.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة