---
title: كيفية إلغاء طلب Fetch في JavaScript (2026)
description: 'إلغاء طلب fetch في JavaScript و React باستخدام AbortController: الإلغاء عند الـ unmount، وإضافة مهلة زمنية باستخدام AbortSignal.timeout، والتعامل مع AbortError بأمان.'
url: https://nerdleveltech.com/ar/cancel-fetch-request-abortcontroller-react
language: ar
date: '2026-06-29'
category: frontend
tags:
  - abortcontroller
  - fetch api
  - javascript
  - react
  - abortsignal
  - async
  - frontend
markdown_url: https://nerdleveltech.com/ar/cancel-fetch-request-abortcontroller-react.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/ar/cancel-fetch-request-abortcontroller-react'
terms: https://nerdleveltech.com/llms.txt
---

# كيفية إلغاء طلب Fetch في JavaScript (2026)

لإلغاء طلب fetch، قم بإنشاء `AbortController`، ومرر الـ `signal` الخاص به إلى `fetch(url, { signal })`، ثم استدعِ `controller.abort()` عندما تريد التوقف. سيتم رفض الـ promise الخاص بالطلب بـ `AbortError` يمكنك تجاهله بأمان. في React، استدعِ `abort()` من وظيفة التنظيف (cleanup) الخاصة بـ `useEffect`.

## ملخص

إلغاء طلب fetch يعتمد على أداة قياسية واحدة في الويب: **`AbortController`**. أنشئ وحدة تحكم (controller)، ومرر `controller.signal` إلى `fetch`، واستدعِ `controller.abort()` لإيقاف الطلب — سيتم رفضه حينها بـ `DOMException` يسمى **`AbortError`**. بالنسبة للمهلات الزمنية (timeouts)، تخطَّ رقصة `setTimeout` القديمة واستخدم **`AbortSignal.timeout(ms)`**، الذي يرفض الطلب بـ **`TimeoutError`**؛ ادمج مهلة زمنية مع زر إلغاء باستخدام **`AbortSignal.any([...])`**. في React، أنشئ وحدة التحكم داخل `useEffect` واستدعِ `abort()` في وظيفة التنظيف لقتل الطلبات القديمة وتجنب ظروف السباق (race conditions). تم التحقق من سلوكيات AbortController الأساسية، والمهلة الزمنية، و `AbortSignal.any()` الموضحة هنا مقابل خادم حي على Node.js 22.

## ما ستتعلمه

- كيفية إلغاء طلب fetch باستخدام `AbortController` و `signal`
- كيفية اكتشاف وتجاهل `AbortError` الذي يطلقه الإلغاء بأمان
- كيفية إلغاء fetch عند إلغاء تثبيت المكون (unmount) وتجنب ظروف السباق في React `useEffect`
- لماذا يتم إطلاق fetch مرتين في بيئة التطوير، ومتى يجب القلق بشأن ذلك
- كيفية إضافة مهلة زمنية للطلب باستخدام `AbortSignal.timeout()`
- كيفية دمج مهلة زمنية وزر إلغاء باستخدام `AbortSignal.any()`
- لماذا يُستخدم `AbortController` لمرة واحدة فقط، وكيفية إعادة استخدام النمط بشكل صحيح
- المتصفحات وبيئات التشغيل التي تدعم هذه الواجهات البرمجية (APIs)
- كيف تتعامل axios و TanStack Query مع الإلغاء

## كيف تلغي طلب fetch في JavaScript؟

أنشئ `AbortController`، ومرر الـ `signal` الخاص به إلى `fetch`، واستدعِ `abort()` عندما تريد إيقاف الطلب. `AbortController` هو الطريقة القياسية والمدمجة لجعل `fetch` قابلاً للإلغاء — لا حاجة لمكتبات خارجية.[^mdn-abortsignal]

```js
// Create a controller and grab its signal
const controller = new AbortController();

fetch("/API/search?q=React", { signal: controller.signal })
  .then((res) => res.json())
  .then((data) => console.log(data))
  .catch((err) => {
    if (err.name === "AbortError") {
      console.log("Request was cancelled");
    } else {
      throw err; // a real network or parsing error
    }
  });

// Later — cancel it
controller.abort();
```

عند استدعاء `abort()`، يتم رفض الـ promise الخاص بـ `fetch()` بـ `DOMException` يسمى `AbortError`.[^mdn-abortsignal] الـ `signal` هو الجانب المخصص للقراءة فقط من وحدة التحكم الذي تسلمه لـ `fetch`؛ بينما يحتفظ الـ `controller` بزر الـ `abort()`. يمكنك تمرير *نفس* الإشارة (signal) لعدة استدعاءات `fetch`، واستدعاء `abort()` واحد سيلغيها جميعاً في وقت واحد — وهو أمر مفيد عندما يؤدي إجراء مستخدم واحد إلى إطلاق مجموعة من الطلبات.

## كيف تتعامل مع AbortError بعد إلغاء fetch؟

قم بالتقاط الرفض (rejection) والتحقق من `err.name === "AbortError"`. الإلغاء هو شيء قمت *أنت* بتفعيله عن قصد، لذا فإن الخطوة المعتادة هي تجاهل هذا الخطأ المحدد وإعادة إطلاق (re-throw) أي شيء آخر. نسخة `async/await` تبدو أكثر وضوحاً:

```js
const controller = new AbortController();

try {
  const res = await fetch(url, { signal: controller.signal });
  const data = await res.json();
  // use data
} catch (err) {
  if (err.name === "AbortError") return; // expected — do nothing
  throw err; // anything else is a genuine failure
}
```

إذا كنت تريد معرفة *سبب* إلغاء الطلب، فقم بتمرير سبب إلى `abort()`. أي قيمة تمررها تصبح هي سبب الرفض ويتم تخزينها أيضاً في `signal.reason`:

```js
controller.abort(new Error("User navigated away"));
```

استدعاء `controller.abort(new Error("User navigated away"))` يجعل الـ fetch يرفض باستخدام كائن الـ `Error` هذا بالضبط — وهو ما تم التحقق منه عن طريق الهوية في الاختبار — بدلاً من الـ `AbortError` الافتراضي.[^mdn-abortsignal] يمكنك أيضاً حماية الكود الذي لا ينبغي تشغيله بمجرد إلغاء الإشارة باستخدام `signal.throwIfAborted()`، والتي تطلق السبب المخزن إذا كانت الإشارة قد أُلغيت بالفعل ولا تفعل شيئاً بخلاف ذلك.[^mdn-abortsignal]

## كيف تلغي طلب fetch في React باستخدام useEffect؟

أنشئ الـ `AbortController` داخل الـ effect، ومرر الـ `signal` الخاص به إلى `fetch`، وأعد وظيفة تنظيف (cleanup function) تستدعي `controller.abort()`. يقوم React بتشغيل وظيفة التنظيف تلك عند إلغاء تثبيت المكون *وقبل* إعادة تشغيل الـ effect، لذا يتم إلغاء الطلب القديم قبل بدء طلب جديد.[^React-effects]

```jsx
import { useEffect, useState } from "React";

function SearchResults({ query }) {
  const [data, setData] = useState(null);

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

    async function load() {
      try {
        const res = await fetch(`/API/search?q=${query}`, {
          signal: controller.signal,
        });
        setData(await res.json());
      } catch (err) {
        if (err.name !== "AbortError") throw err;
      }
    }
    load();

    return () => controller.abort(); // cancel on unmount or when query changes
  }, [query]);

  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
```

هذا يحل خطأين حقيقيين. الأول هو **ظرف السباق (race condition)**: إذا تغيرت الـ `query` من `"alice"` إلى `"bob"`، فقد يصل استجابة `"alice"` الأبطأ في النهاية وتكتب فوق `"bob"`. إلغاء الطلب القديم يجعل ذلك مستحيلاً. الثاني هو النمط الكلاسيكي "لا يمكن تحديث الحالة في مكون غير مثبت"، لأن الطلب الملغى لا يصل أبداً إلى `setData`.

تقدم وثائق React نمطاً ثانياً — وهو علامة `ignore` — يمكنك استخدامه بدلاً من الإلغاء، أو بجانبه:[^React-effects]

```jsx
useEffect(() => {
  let ignore = false;
  fetch(`/API/search?q=${query}`)
    .then((res) => res.json())
    .then((data) => {
      if (!ignore) setData(data);
    });
  return () => {
    ignore = true;
  };
}, [query]);
```

الفرق ملموس: علامة `ignore` *تتجاهل* الاستجابة القديمة ولكن طلب الشبكة يكتمل، بينما `AbortController` يقوم فعلياً *بإلغاء* الطلب ويوفر عرض النطاق الترددي (bandwidth). الإلغاء هو الخيار الأقوى عندما تكون الاستجابات كبيرة أو مكلفة.

## لماذا يتم تشغيل fetch مرتين أو إلغاؤه فوراً في React؟

بسبب `<StrictMode>` في بيئة التطوير. يقوم React عمداً باستدعاء الـ effects مرتين في بيئة التطوير — الإعداد (setup)، ثم التنظيف (cleanup)، ثم الإعداد مرة أخرى — لمساعدتك في اكتشاف عمليات التنظيف المفقودة.[^React-strict] مع وجود تنظيف `AbortController`، فهذا يعني أن طلبك الأول يتم إلغاؤه فوراً تقريباً، لذا قد ترى طلباً إضافياً و `AbortError` (أو إدخال "canceled" في علامة تبويب الشبكة Network) أثناء التطوير.

هذا أمر متوقع ويكون **في بيئة التطوير فقط**. في بيئة الإنتاج، يتم تشغيل الـ effect مرة واحدة ولا يتم إلغاء أي شيء قبل الأوان. حقيقة أنك ترى الإلغاء تثبت أن وظيفة التنظيف الخاصة بك تعمل — وهذا ليس خطأ يجب "إصلاحه" عن طريق حذف `StrictMode` أو وظيفة التنظيف. وبما أن المثال أعلاه يتجاهل بالفعل `AbortError`، فإن الطلب المزدوج لا يسبب أي مشكلة مرئية للمستخدمين.

## كيف تضيف مهلة زمنية لطلب fetch؟

مرر `AbortSignal.timeout(ms)` كـ `signal`. لا يحتوي معيار Fetch على خيار `timeout` مدمج، لذا فإن هذه الطريقة الثابتة (static method) هي الطريقة الحديثة والرسمية لإعطاء الطلب موعداً نهائياً.[^fetch-spec] يتم إلغاء الإشارة تلقائياً بعد عدد المللي ثانية المحدد ويرفض الـ fetch بـ `TimeoutError` `DOMException`.[^mdn-timeout]

التفصيلة الأساسية هي اسم الخطأ. تنتهي المهلة (timeout) برفض يحمل الاسم `TimeoutError`، بينما الإلغاء الذي يطلبه المستخدم يرفض بـ `AbortError`، لذا يمكن لكتلة `catch` واحدة التمييز بينهما — وهو ما تم التحقق منه بتشغيل مهلة 50 مللي ثانية مقابل نقطة نهاية تستغرق ثانية واحدة.[^mdn-timeout] هذا التمييز مهم: فانتهاء المهلة يستحق عادةً رسالة "حاول مرة أخرى"، بينما الإلغاء المتعمد يستحق الصمت عادةً. (ملاحظة تاريخية: المتصفحات القائمة على Chromium قبل إصدار Chrome 124 كانت تبلغ عن انتهاء مهلة fetch كـ `AbortError` بدلاً من `TimeoutError`؛ المتصفحات الحالية تتبع المواصفات.[^chrome-fix]) هذا يحل محل النمط القديم المتمثل في إنشاء `AbortController`، وبدء `setTimeout` يستدعي `abort()`، ومسح المؤقت يدوياً.

## كيف تجمع بين مهلة زمنية وزر إلغاء؟

استخدم `AbortSignal.any()` لدمج عدة إشارات في إشارة واحدة. تأخذ مصفوفة من الإشارات وتعيد إشارة يتم إلغاؤها بمجرد إلغاء *أي* منها — وهي مثالية لسيناريو "الإلغاء بعد 10 ثوانٍ **أو** عندما ينقر المستخدم على إلغاء".[^mdn-any]

```js
const controller = new AbortController();
cancelButton.addEventListener("click", () => controller.abort());

const res = await fetch(url, {
  signal: AbortSignal.any([controller.signal, AbortSignal.timeout(10_000)]),
});
```

تتبنى الإشارة المدمجة سبب الإلغاء الخاص بأي إشارة مصدر يتم إلغاؤها أولاً.[^mdn-any] هناك ملاحظة تستحق المعرفة: تشير MDN إلى أنه، على عكس `AbortSignal.timeout()` المنفردة، لا يمكنك دائماً الاعتماد على اسم الخطأ وحده لمعرفة ما إذا كان الإلغاء *المدمج* ناتجاً عن انتهاء المهلة.[^mdn-abortsignal] عندما تحتاج إلى معرفة السبب بيقين، تحقق من أي وحدة تحكم (controller) تم تفعيلها بدلاً من اسم الخطأ:

```js
} catch (err) {
  if (controller.signal.aborted) {
    console.log("Cancelled by the user");
  } else {
    console.log("Request timed out");
  }
}
```

تكون قيمة `controller.signal.aborted` هي `true` فقط عندما يتم تشغيل معالج الإلغاء الخاص بك، لذا فهي تفصل بين الإلغاء اليدوي وانتهاء المهلة بشكل موثوق عبر المحركات المختلفة. في كلتا الحالتين، تحصل على إلغاء متعدد الطبقات — مهلة زمنية *و* يدوي — من إشارة `signal` واحدة، دون الحاجة للتعامل مع المؤقتات بنفسك.

## هل يمكنك إعادة استخدام AbortController لطلبات متعددة؟

لا — إن `AbortController` (وإشارته) مخصص للاستخدام لمرة واحدة فقط. كما تذكر MDN، الإشارة "يمكن استخدامها مرة واحدة فقط"؛ بعد إلغائها، يتم رفض أي fetch يستخدم نفس الإشارة فوراً.[^mdn-abortsignal] في الاختبارات، أدى تمرير إشارة ملغاة بالفعل إلى طلب `fetch` جديد إلى رفضه فوراً بـ `AbortError`.

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

```js
// One controller per request you might cancel on its own
function startRequest(url) {
  const controller = new AbortController();
  const promise = fetch(url, { signal: controller.signal });
  return { promise, cancel: () => controller.abort() };
}

const { promise, cancel } = startRequest("/API/report");
// cancel(); // stops just this request
```

## هل AbortController مدعوم في جميع المتصفحات و Node.js؟

تعتبر ميزات `AbortController` و `AbortSignal` و `fetch` القابل للإلغاء من ميزات Baseline "المتاحة على نطاق واسع"؛ تدرج MDN أن `AbortController` متاح عبر المتصفحات منذ مارس 2019، لذا فإن نمط الإلغاء الأساسي آمن للاستخدام في كل مكان اليوم.[^mdn-abortcontroller] المساعدان الجديدان أحدث عهداً: تدرج MDN أن `AbortSignal.timeout()` ضمن Baseline 2024 (متاح منذ أبريل 2024) و `AbortSignal.any()` ضمن Baseline 2024 (متاح منذ مارس 2024).[^mdn-timeout][^mdn-any]

إذا كان عليك دعم متصفحات قديمة تفتقر إلى `AbortSignal.timeout()`، فإن الحل البديل الكلاسيكي لا يزال يعمل: أنشئ `AbortController`، وابدأ `setTimeout` يستدعي `controller.abort()`، وامسح المؤقت بمجرد اكتمال الطلب. هذه الواجهات البرمجية موجودة أيضاً في بيئات تشغيل الخادم الحديثة — Node.js و Deno و Bun — لذا فإن نفس كود الإلغاء يعمل على الخادم.

## هل يقوم axios و TanStack Query بإلغاء الطلبات بنفس الطريقة؟

نعم — كلاهما يعتمد على نفس معيار `AbortController`، لذا فإن المفهوم ينتقل مباشرة. مع **axios**، تقوم بتمرير إشارة `AbortController` في إعدادات الطلب؛ تم إيقاف دعم `CancelToken` القديم في الإصدار v0.22.0 (أكتوبر 2021) ويجب أن تفضل نهج الإشارة.[^axios]

```js
const controller = new AbortController();
axios.get("/API/search", { signal: controller.signal });
controller.abort();
```

مع **TanStack Query**، لا تقوم عادةً بإنشاء وحدة تحكم على الإطلاق: تمرر المكتبة `AbortSignal` إلى دالة الاستعلام الخاصة بك وتلغيها تلقائياً عندما يصبح الاستعلام قديماً أو غير نشط. ما عليك سوى تمرير تلك الإشارة إلى `fetch`.[^tanstack]

```js
useQuery({
  queryKey: ["todos"],
  queryFn: async ({ signal }) => {
    const res = await fetch("/API/todos", { signal });
    return res.json();
  },
});
```

## الأسئلة الشائعة

**كيف تلغي طلب fetch في JavaScript؟** أنشئ `AbortController`، ومرر `controller.signal` إلى `fetch(url, { signal })`، واستدعِ `controller.abort()` للإلغاء. عندها يتم رفض وعد (promise) الـ fetch بـ `DOMException` يسمى `AbortError`.[^mdn-abortsignal]

**كيف أمنع ظهور AbortError كخطأ؟** قم بالتقاطه والتحقق من الاسم: `if (err.name === "AbortError") return;`. الإلغاء مقصود، لذا تجاهل هذا الخطأ تحديداً وأعد رمي البقية.[^mdn-abortsignal]

**كيف ألغي fetch عند إلغاء تثبيت (unmount) مكون React؟** أنشئ وحدة التحكم داخل `useEffect`، ومرر إشارتها إلى `fetch`، وأعد `() => controller.abort()` كدالة تنظيف. يستدعيها React عند إلغاء التثبيت وقبل إعادة تشغيل التأثير (effect).[^react-effects]

**لماذا يتم تشغيل fetch مرتين في بيئة التطوير؟** يقوم `<StrictMode>` في React باستدعاء التأثيرات مرتين في بيئة التطوير للكشف عن عمليات التنظيف المفقودة، لذا يتم تشغيل طلبك، ثم إلغاؤه، ثم تشغيله مرة أخرى. هذا يحدث في بيئة التطوير فقط ولا يحدث في الإنتاج.[^react-strict]

**كيف أضيف مهلة زمنية (timeout) لـ fetch؟** مرر `AbortSignal.timeout(ms)` كإشارة. لا يحتوي `fetch` على خيار مهلة مدمج، وتنتهي المهلة برفض يحمل الاسم `TimeoutError`.[^mdn-timeout][^fetch-spec]

**هل يمكنني الجمع بين مهلة زمنية (timeout) وإلغاء يدوي؟** نعم. استخدم `AbortSignal.any([controller.signal, AbortSignal.timeout(ms)])`؛ سيتم إلغاء الطلب عند تفعيل أي منهما، مع اعتماد سبب الإلغاء الخاص بالأسرع بينهما.[^mdn-any]

**هل يمكنني إعادة استخدام AbortController بعد استدعاء abort()؟** لا. الإشارة (signal) صالحة للاستخدام مرة واحدة فقط — بمجرد إلغائها، سيتم رفض أي عملية fetch تستخدمها فوراً، لذا قم بإنشاء متحكم (controller) جديد لكل عملية قابلة للإلغاء.[^mdn-abortsignal]

## الخلاصة

إلغاء عملية fetch هو مهمة تعتمد على أداة واحدة: `AbortController` للإلغاء اليدوي، و `AbortSignal.timeout()` للمواعيد النهائية، و `AbortSignal.any()` للجمع بينهما — وكلها معايير قياسية، مدمجة، ومدعومة في المتصفحات وبيئات التشغيل الحديثة. في React، يختصر النمط بالكامل في "إنشاء متحكم داخل `useEffect`، وإلغاؤه في دالة التنظيف (cleanup)"، مما يقضي على الطلبات القديمة وحالات السباق (race conditions) التي تصاحبها.

للتعمق أكثر في الأسس غير المتزامنة (async) التي يقوم عليها كل هذا، راجع [كيفية عمل الـ callbacks والـ promises والـ async/await](https://nerdleveltech.com/understand-the-asynchronous-JavaScript-callbacks-promises-and-async-await) ودليلنا حول [أنماط الـ async الحديثة في JavaScript](https://nerdleveltech.com/mastering-JavaScript-async-patterns-from-callbacks-to-concurrency). وإذا كنت تفضل ترك مكتبة تتعامل مع الإلغاء، والتخزين المؤقت (caching)، وحالات السباق نيابة عنك، فراجع كيف [تدير TanStack Query الطلبات والتحديثات المتفائلة (optimistic updates)](https://nerdleveltech.com/tanstack-query-optimistic-updates-rollback-tutorial).

[^mdn-abortsignal]: MDN Web Docs — AbortSignal interface (abort behavior, single-use signals, `reason`, `throwIfAborted`). <https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal>

[^React-effects]: React — Synchronizing with Effects (fetch cleanup, ignore vs. abort, race conditions). <https://React.dev/learn/synchronizing-with-effects>

[^React-strict]: React — `<StrictMode>` (re-runs Effects an extra time in development). <https://React.dev/reference/React/StrictMode>

[^fetch-spec]: WHATWG Fetch Standard — "Add a `timeout` option, to prevent hanging" (issue #951; fetch has no built-in timeout option). <https://GitHub.com/whatwg/fetch/issues/951>

[^mdn-timeout]: MDN Web Docs — AbortSignal: timeout() static method (TimeoutError on timeout, Baseline 2024). <https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static>

[^chrome-fix]: mdn/browser-compat-data issue #20381 — `AbortSignal.timeout()` surfaced `AbortError` instead of `TimeoutError` in Chromium before Chrome 124. <https://GitHub.com/mdn/browser-compat-data/issues/20381>

[^mdn-any]: MDN Web Docs — AbortSignal: any() static method (combine signals, first-reason wins, Baseline 2024). <https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static>

[^mdn-abortcontroller]: MDN Web Docs — AbortController interface (Baseline "widely available," across browsers since March 2019). <https://developer.mozilla.org/en-US/docs/Web/API/AbortController>

[^axios]: Axios — Cancellation (AbortController `signal`; `CancelToken` deprecated since v0.22.0). <https://axios-http.com/docs/cancellation>

[^tanstack]: TanStack Query — إلغاء الاستعلام (`AbortSignal` يتم تمريره إلى دالة الاستعلام). <https://tanstack.com/query/latest/docs/framework/React/guides/query-cancellation>
