
# أنماط AbortController

تُنفذ `AbortController` و`AbortSignal` بالكامل وفق WHATWG في Phasis. كما تتوفر أدوات التركيب القياسية (`AbortSignal.any` و`AbortSignal.timeout`). تتناول هذه الصفحة الأنماط المستخدمة فعليًا وكيف ينتشر الإلغاء عبر الحد الفاصل بين PHP.

## إلغاء طلب fetch

```js
const ctrl = new AbortController();
const p = fetch(url, { signal: ctrl.signal });
ctrl.abort();                     // p rejects with AbortError DOMException
```

السبب هو `AbortError` افتراضيًا، لكن يمكنك تحديد سببك:

```js
ctrl.abort(new Error('user cancelled'));
```

يرفض `fetch` بالقيمة التي مررتها إليه.

## المهلة الزمنية

```js
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
```

يعيد `AbortSignal.timeout(ms)` إشارة تلغي نفسها بعد `ms`. يعمل المؤقت عبر حلقة أحداث المحرك نفسها التي تشغّل `setTimeout`/`queueMicrotask` (راجع [API: حلقة الأحداث](https://phasis.inline0.dev/ar/docs/api#event-loop)). يتحقق ناقل fetch من الإشارة أثناء النقل — وبالنسبة إلى `CurlTransport` الافتراضي، عبر `CURLOPT_PROGRESSFUNCTION` — لذا يُلغى الطلب سريعًا عند حلول الموعد النهائي.

تعمل الحلقة وفق **الوقت الافتراضي** افتراضيًا: تقدّم ساعتها إلى الموعد النهائي المعلّق التالي بدلًا من الانتظار PHP، لذا إما أن يكتمل `fetch(url, { signal: AbortSignal.timeout(5000) })` قبل الموعد النهائي أو يُلغى عند نبضة الحلقة التالية — ولا يجعل PHP ينتظر. يتطلب ضبط الإيقاع وفق الوقت الفعلي أن يقود المضيف `tickEventLoop()` وفق ساعة فعلية.

## التسابق: الإلغاء الأول هو الفائز

```js
const user = new AbortController();
const signal = AbortSignal.any([user.signal, AbortSignal.timeout(10_000)]);
const res = await fetch(url, { signal });
// res rejects if EITHER the user clicks cancel OR 10s elapses
```

## الانتشار عبر الطبقات

غلّف عملك غير المتزامن بحلقة تراعي الإشارة:

```js
async function fetchPaged(url, signal) {
  const out = [];
  for (let page = 1; ; page++) {
    if (signal.aborted) throw signal.reason;
    const r = await fetch(`${url}?page=${page}`, { signal });
    const data = await r.json();
    if (data.length === 0) break;
    out.push(...data);
  }
  return out;
}
```

يرث طلب fetch الإشارة مباشرة؛ أما `if (signal.aborted)` اليدوي فيغطي الفجوات بين الطلبات التي قد تواصل فيها حلقة الصفحة التنفيذ لولا ذلك.

## الاستماع إلى حدث الإلغاء

```js
ctrl.signal.addEventListener('abort', () => {
  releaseDatabaseLock();
  cancelDownstreamWork();
});
```

تُضبط `signal.reason` قبل تشغيل المستمعين.

## الإشارة في ناقل fetch مخصص

إذا بدّلت وسيلة النقل عبر `setFetchTransport()`، فستُمرر الإشارة إلى الدالة القابلة للاستدعاء لديك بوصفها `JsObject`. احترمها بالتحقق منها دوريًا:

```php
$engine->setFetchTransport(function (array $req, $signal) {
    $ch = curl_init($req['url']);
    // ...
    if ($signal !== null) {
        curl_setopt($ch, CURLOPT_PROGRESSFUNCTION,
            fn () => $signal->get('aborted')->value ? 1 : 0);
        curl_setopt($ch, CURLOPT_NOPROGRESS, false);
    }
    $body = curl_exec($ch);
    if (curl_errno($ch) === CURLE_ABORTED_BY_CALLBACK) {
        throw new \Phasis\BuiltIn\Fetch\TransportException(
            'aborted', 'aborted',
        );
    }
    // ...
});
```

يؤدي إطلاق `TransportException` باستخدام `kind: 'aborted'` إلى رفض `fetch` في JS بقيمة `AbortError DOMException` — كما لو نُفذت حلقة الإلغاء المحددة في المواصفات.

بالنسبة إلى الواجهات الخلفية ذات الإلغاء الأصلي (`on_progress` من Guzzle و`ResponseInterface::cancel` من Symfony)، استخدم نمط التحقق ثم الإطلاق نفسه.

## أنماط لا تعمل بعد

- **`signal.addEventListener('abort', () => fetch(cleanupUrl))`** — تشغيل المستمع متزامن؛ يبدأ طلب fetch للتنظيف لكنه لن يكتمل أثناء إرسال حدث الإلغاء. جدوِله عبر `await` المحيطة.

يعمل `setTimeout(() => ctrl.abort(), N)` (عائلة المؤقتات متاحة ومغطاة باختبارات WPT)، لكن `AbortSignal.timeout(N)` أقصر ويعبّر عن المقصود بوضوح أكبر عندما تريد فقط إشارة إلغاء بعد الموعد النهائي.

## راجع أيضًا

- [ناقل Fetch](https://phasis.inline0.dev/ar/docs/interop/fetch-transport) — واجهة كاملة للطلب والاستجابة والإشارة للناقلات المخصصة.
- [واجهات الويب (WPT)](https://phasis.inline0.dev/ar/docs/compatibility/web-apis) — معدل اجتياز اختبارات WPT لـ AbortController/AbortSignal.
