
# Mönster för AbortController

`AbortController` och `AbortSignal` är fullständiga WHATWG-implementationer i Phasis. Standardhjälpfunktionerna för komposition (`AbortSignal.any`, `AbortSignal.timeout`) ingår också. Den här sidan går igenom de mönster du faktiskt använder och hur avbrott sprids över gränsen till PHP.

## Avbryta en fetch

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

Orsaken är `AbortError` som standard, men du kan ange en egen:

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

`fetch` avvisar med det värde du skickade in.

## Tidsgräns

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

`AbortSignal.timeout(ms)` returnerar en signal som avbryter sig själv efter `ms`. Timern utlöses via motorns händelseloop, samma loop som driver `setTimeout` / `queueMicrotask` (se [API: Händelseloop](https://phasis.inline0.dev/sv/docs/api#event-loop)). Fetch-transporten läser av signalen under överföringen — för standardtransporten `CurlTransport` via `CURLOPT_PROGRESSFUNCTION` — så begäran avbryts snabbt när tidsgränsen nås.

Loopen använder **virtuell tid** som standard: den flyttar klockan fram till nästa väntande tidsgräns i stället för att vänta PHP, så en `fetch(url, { signal: AbortSignal.timeout(5000) })` antingen slutförs före tidsgränsen eller avbryts vid nästa loopvarv — den låter aldrig PHP vänta. För att hålla jämn takt i realtid måste värden driva `tickEventLoop()` mot en väggklocka.

## Kapplöpning: den första avbrytningen vinner

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

## Propagering genom lager

Omslut ditt eget asynkrona arbete med en signalmedveten loop:

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

Hämtningen ärver signalen direkt; den manuella `if (signal.aborted)` täcker luckor mellan förfrågningar då sidloopen annars skulle fortsätta köra.

## Lyssna efter avbrytningshändelsen

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

`signal.reason` är inställd innan lyssnarna anropas.

## Signal i en anpassad fetch-transport

Om du byter transport via `setFetchTransport()` skickas signalen till din anropsbara funktion som en `JsObject`. Hantera den genom att kontrollera den regelbundet:

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

Om du kastar `TransportException` med `kind: 'aborted'` avvisas JS-sidans `fetch` med `AbortError DOMException` — precis som om specifikationens avbrytningsloop hade körts.

För backendlösningar med inbyggt stöd för avbrytning (Guzzles `on_progress`, Symfonys `ResponseInterface::cancel`), använd samma mönster med kontroll och kastat undantag.

## Mönster som ännu inte fungerar

- **`signal.addEventListener('abort', () => fetch(cleanupUrl))`** — lyssnare anropas synkront; rensningshämtningen startar men hinner inte slutföras under avbrytningshanteringen. Schemalägg den via den omgivande `await`.

`setTimeout(() => ctrl.abort(), N)` fungerar (timerfunktionerna ingår och täcks av WPT), men `AbortSignal.timeout(N)` är kortare och uttrycker avsikten bättre när du bara vill ha en signal som avbryter efter en tidsgräns.

## Se även

- [Fetch-transport](https://phasis.inline0.dev/sv/docs/interop/fetch-transport) — komplett gränssnitt för begäran/svar/signal för anpassade transporter.
- [Web APIs (WPT)](https://phasis.inline0.dev/sv/docs/compatibility/web-apis) — godkännandefrekvens för AbortController/AbortSignal i WPT.
