Hoppa till innehåll

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

JavaScript
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:

JavaScript
ctrl.abort(new Error('user cancelled'));

fetch avvisar med det värde du skickade in.

Tidsgräns

JavaScript
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). 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

JavaScript
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:

JavaScript
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

JavaScript
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 — komplett gränssnitt för begäran/svar/signal för anpassade transporter.
  • Web APIs (WPT) — godkännandefrekvens för AbortController/AbortSignal i WPT.