Abort و Cancellation: لغو واقعی یک درخواست

در درس قبل دیدیم پرچم ignore فقط جلوی اعمال نتیجه یک درخواست قدیمی را می‌گیرد، اما خود درخواست همچنان در پس‌زمینه ادامه پیدا می‌کند. در این درس، ابزاری را می‌بینیم که اجازه می‌دهد خود درخواست شبکه‌ای را واقعاً متوقف کنیم: AbortController.

چرا گاهی لغو واقعی مهم است

اگر کاربر به‌سرعت چند بار جست‌وجو را عوض کند، یا به‌سرعت بین چند صفحه جابه‌جا شود، ممکن است چندین درخواست هم‌زمان در حال اجرا باشند که هیچ‌کدام، به‌جز آخری، دیگر لازم نیستند. ادامه دادن این درخواست‌های بی‌فایده، پهنای باند کاربر و منابع سرور را هدر می‌دهد؛ مخصوصاً برای درخواست‌های سنگین یا تعداد بالای کاربران.

AbortController: ابزار استاندارد مرورگر برای لغو

AbortController بخشی از خود جاوااسکریپت و مرورگر است، نه چیزی مخصوص React. یک Controller می‌سازیم، signal آن را به fetch می‌دهیم، و در Cleanup Effect، متد abort() را فراخوانی می‌کنیم:

useEffect(() => {
  const controller = new AbortController();

  fetch(`/api/search?q=${query}`, { signal: controller.signal })
    .then((res) => res.json())
    .then((data) => setResults(data))
    .catch((err) => {
      if (err.name !== "AbortError") {
        setError(err.message);
      }
    });

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

وقتی query تغییر کند و Cleanup اجرای قبلی فراخوانی شود، controller.abort() همان درخواست در حال اجرا را واقعاً لغو می‌کند؛ مرورگر دیگر منتظر پاسخ آن نمی‌ماند.

چرا باید AbortError را جدا بررسی کرد

وقتی یک درخواست abort می‌شود، fetch آن را به‌عنوان یک خطا در نظر می‌گیرد و وارد بلوک catch می‌شود. اما این خطا، یک شکست واقعی نیست؛ فقط نتیجه لغو عمدی ماست. به همین دلیل، باید نام خطا را بررسی کرد و خطای ناشی از abort را نادیده گرفت، نه اینکه آن را به کاربر نمایش دهیم.

مقایسه با راه‌حل پرچم ignore

این دو روش، هدف مشابهی دارند اما سطح اثرشان فرق می‌کند:

  • پرچم ignore: ساده‌تر، درخواست همچنان کامل می‌شود، فقط نتیجه‌اش نادیده گرفته می‌شود.
  • AbortController: خود درخواست واقعاً متوقف می‌شود؛ مناسب‌تر برای درخواست‌های سنگین یا پرتکرار، مثل جست‌وجوی زنده.

برای بسیاری از برنامه‌های کوچک و متوسط، پرچم ignore کاملاً کافی است. AbortController زمانی ارزش پیچیدگی اضافه‌اش را دارد که حجم یا فرکانس درخواست‌ها واقعاً قابل توجه باشد.

کتابخانه‌های دریافت داده، این کار را خودکار انجام می‌دهند

نوشتن دستی AbortController برای هر Effect، تکراری و مستعد خطا است. همان‌طور که در درس‌های بعدی همین فصل خواهیم دید، بسیاری از کتابخانه‌های تخصصی دریافت داده، لغو درخواست قدیمی را به‌طور خودکار و پشت‌صحنه مدیریت می‌کنند.

مثال قابل اجرا

لغو واقعی درخواست قبلی با AbortController
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

AbortController ابزار استاندارد مرورگر برای لغو واقعی یک درخواست fetch در حال اجرا است؛ با فراخوانی abort() در Cleanup یک Effect، درخواست قدیمی متوقف می‌شود، نه فقط نادیده گرفته. باید خطای ناشی از abort را جدا از خطاهای واقعی بررسی کرد. در درس بعدی به Caching می‌پردازیم: چگونه از ارسال درخواست‌های تکراری برای داده‌ای که قبلاً گرفته‌ایم جلوگیری کنیم.