
# Vzory použití AbortController

`AbortController` a `AbortSignal` jsou v Phasis plně implementovány podle WHATWG. K dispozici jsou i standardní pomocné funkce pro skládání (`AbortSignal.any`, `AbortSignal.timeout`). Tato stránka popisuje běžně používané vzory a šíření zrušení přes hranici PHP.

## Zrušení požadavku fetch

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

Výchozím důvodem je `AbortError`, ale můžete zadat vlastní:

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

`fetch` vyvolá zamítnutí s předanou hodnotou.

## Časový limit

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

`AbortSignal.timeout(ms)` vrací signál, který se sám zruší po `ms`. Časovač se spouští prostřednictvím smyčky událostí enginu, tedy stejné smyčky, která řídí `setTimeout` / `queueMicrotask` (viz [API: Smyčka událostí](https://phasis.inline0.dev/cs/docs/api#event-loop)). Fetch transport během přenosu signál kontroluje — u výchozího `CurlTransport` prostřednictvím `CURLOPT_PROGRESSFUNCTION` — takže se požadavek po uplynutí lhůty rychle zruší.

Smyčka ve výchozím nastavení používá **virtuální čas**: místo skutečného čekání PHP posune hodiny k nejbližší nevyřízené lhůtě, takže se `fetch(url, { signal: AbortSignal.timeout(5000) })` buď dokončí před lhůtou, nebo se zruší při dalším kroku smyčky — nikdy nenechá PHP čekat. Pro běh v reálném čase musí hostitel řídit `tickEventLoop()` podle skutečných hodin.

## Závod: vyhrává první zrušení

```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
```

## Šíření mezi vrstvami

Obalte vlastní asynchronní práci smyčkou, která reaguje na signál:

```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;
}
```

Požadavek fetch signál zdědí přímo; ruční `if (signal.aborted)` pokrývá prodlevy mezi požadavky, kdy by jinak smyčka stránky pokračovala.

## Naslouchání události zrušení

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

`signal.reason` se nastaví ještě před vyvoláním posluchačů.

## Signál ve vlastním fetch transportu

Pokud přes `setFetchTransport()` vyměníte transport, signál se předá vaší volatelné funkci jako `JsObject`. Respektujte ho pravidelnou kontrolou:

```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',
        );
    }
    // ...
});
```

Vyvoláním `TransportException` s `kind: 'aborted'` způsobíte, že JS-ová strana `fetch` odmítne `AbortError DOMException` — stejně, jako kdyby se spustila smyčka rušení podle specifikace.

U backendů s nativním rušením (Guzzle's `on_progress`, Symfony's `ResponseInterface::cancel`) použijte stejný vzor kontroly a vyvolání chyby.

## Vzory, které zatím nefungují

- **`signal.addEventListener('abort', () => fetch(cleanupUrl))`** — posluchač se spouští synchronně; úklidový požadavek fetch začne, ale během zpracování události zrušení se nedokončí. Naplánujte ho přes okolní `await`.

`setTimeout(() => ctrl.abort(), N)` funguje (sada časovačů je součástí implementace a pokrývají ji WPT), ale `AbortSignal.timeout(N)` je kratší a lépe vystihuje záměr, pokud potřebujete jen signál, který se zruší po uplynutí lhůty.

## Viz také

- [Fetch transport](https://phasis.inline0.dev/cs/docs/interop/fetch-transport) — úplné rozhraní požadavků, odpovědí a signálů pro vlastní transporty.
- [Webová API (WPT)](https://phasis.inline0.dev/cs/docs/compatibility/web-apis) — míra úspěšnosti testů WPT pro AbortController/AbortSignal.
