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

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

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

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

المنصة

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

الدعم

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

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

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

AbortController بالعربي: امنع نتائج البحث القديمة من كسر الواجهة

📅 ٢٥ أبريل ٢٠٢٦⏱ 4 دقائق قراءة
AbortController بالعربي: امنع نتائج البحث القديمة من كسر الواجهة

AbortController بالعربي: امنع نتائج البحث القديمة من كسر الواجهة

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

هتخرج من المقال ده بطريقة عملية تمنع طلبات fetch القديمة من تحديث الواجهة بنتائج غلط، خصوصًا في search box أو filters بتتغير بسرعة.

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

افترض إن عندك صفحة منتجات. المستخدم كتب iph، وبعد 200ms كتب iphone. الواجهة بعتت طلبين: طلب قديم لكلمة iph وطلب جديد لكلمة iphone. اللي بيحصل فعلاً إن الشبكة مش بتضمن ترتيب الرجوع. ممكن الطلب القديم يرجع بعد الجديد، فيعمل setState ويعرض نتائج مش مطابقة لآخر كلمة كتبها المستخدم.

الطريقة الشائعة الغلط هنا إنك تعتمد على إن آخر طلب اتبعت هو آخر طلب هيرجع. الطريقة دي بتفشل مع latency متغير، CDN، mobile network، أو backend عليه ضغط. ركز: المشكلة مش بس في استهلاك الشبكة. المشكلة الأكبر إن UI ممكن تبقى كاذبة.

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

الفكرة بمثال بسيط

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

في JavaScript، الإشارة دي اسمها AbortSignal، واللي بيصدرها هو AbortController. بتنشئ controller لكل طلب، تمرر controller.signal إلى fetch، ولما الطلب يبقى قديم تستدعي controller.abort(). حسب توثيق MDN، abort() يقدر يوقف طلب fetch وقراءة جسم الاستجابة والـ streams المرتبطة به.

تطبيق فعلي في React

الافتراض إن عندك React component بيجيب نتائج بحث من endpoint اسمه /api/search?q=.... كل مرة query تتغير، هنلغي الطلب السابق قبل ما نبدأ طلب جديد. ده يطابق فكرة cleanup في useEffect: React يشغّل cleanup للـ effect القديم قبل تشغيل effect الجديد عند تغيّر dependencies.

JavaScript
import { useEffect, useState } from "react";

export function SearchResults({ query }) {
  const [results, setResults] = useState([]);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!query.trim()) {
      setResults([]);
      return;
    }

    const controller = new AbortController();

    async function load() {
      setLoading(true);
      setError(null);

      try {
        const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
          signal: controller.signal,
        });

        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const data = await res.json();
        setResults(data.items);
      } catch (err) {
        if (err.name === "AbortError") return;
        setError(err.message);
      } finally {
        if (!controller.signal.aborted) setLoading(false);
      }
    }

    load();

    return () => controller.abort();
  }, [query]);

  return null;
}

في المثال ده، لو المستخدم كتب 8 حروف في ثانية واحدة، من غير إلغاء ممكن يبقى عندك 8 طلبات نشطة. مع AbortController غالبًا هيفضل طلب واحد مهم: آخر query فقط. الرقم تقديري لكنه واقعي في search boxes اللي بتعمل request مع كل تغيير.

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

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

في بعض الواجهات، الطلب البطيء أسوأ من فشل واضح. مثال: autocomplete لازم يرد خلال 300 إلى 800ms. لو استنى 5 ثواني، المستخدم أصلًا كمل كتابة أو غادر الصفحة.

لو بيئتك تدعم AbortSignal.timeout()، تقدر تعمل timeout مباشر. MDN يذكر إن هذه الدالة ترجع signal يتم إلغاؤه تلقائيًا بعد عدد محدد من المللي ثانية، وهي Baseline 2024. لو بتدعم متصفحات قديمة، استخدم fallback يدوي بـ setTimeout.

JavaScript
const res = await fetch("/api/search?q=iphone", {
  signal: AbortSignal.timeout(800),
});

الـ trade-off هنا واضح: هتكسب واجهة أسرع وفشلًا قابلًا للتحكم، لكن ممكن تقطع طلبًا كان هينجح بعد 900ms. لذلك لا تختار timeout عدواني من غير قياس P95 latency عندك.

هل ده بديل لـ Debounce؟

لا. Debounce يقلل عدد الطلبات قبل ما تخرج من المتصفح. AbortController يتعامل مع الطلبات اللي خرجت بالفعل وبقت قديمة. أفضل طريقة في search box مزدحم هي استخدام الاثنين معًا: Debounce بسيط 200ms، ثم AbortController لإلغاء أي طلب سابق.

لو عندك موقع بـ 50K زيارة يوميًا، وكل مستخدم يعمل 10 عمليات بحث، تقليل 5 طلبات زائدة لكل جلسة يعني 250K طلب أقل يوميًا. المكسب: ضغط أقل على API ونتائج أدق. الخسارة: كود أكثر قليلًا، ولازم تتعامل مع AbortError كحالة طبيعية مش خطأ يستحق alert.

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

لا تستخدمها لإلغاء عمليات لازم تكتمل على السيرفر، مثل الدفع أو إنشاء order، إلا لو backend عنده idempotency وتصميم واضح للتعامل مع الإلغاء. كمان لا تعتمد عليها كوسيلة حماية أمنية. إلغاء الطلب من المتصفح لا يضمن إن السيرفر لم يستقبل الطلب أصلًا.

لو بتستخدم TanStack Query أو SWR أو React Router loaders، راجع آلية الإلغاء والكاش داخل الأداة قبل ما تضيف controller يدوي في كل component. أحيانًا المكتبة already بتديك signal أو بتمنع stale updates بطريقة أفضل.

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

  • MDN: AbortController.abort()
  • MDN: AbortSignal
  • MDN: AbortSignal.timeout()
  • React Docs: useEffect cleanup
  • React Docs: Race conditions in Effects

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

افتح أول component عندك فيه fetch مع useEffect وdependency متغيرة، وضيف AbortController في cleanup. بعد كده افتح Network tab واكتب بسرعة. لو الطلبات القديمة بقت canceled والواجهة تعرض آخر query فقط، التنفيذ شغال بالظبط.

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

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

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