Gå til indhold

AbortController-mønstre

AbortController og AbortSignal er fulde WHATWG-implementeringer i Phasis. Standardens kompositionshjælpere (AbortSignal.any, AbortSignal.timeout) følger også med. Denne side gennemgår de mønstre, du faktisk bruger, og hvordan annullering føres videre på tværs af grænsen PHP.

Annullering af et fetch-kald

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

Årsagen er som standard AbortError, men du kan angive din egen:

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

fetch afviser med den værdi, du sendte.

Timeout

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

AbortSignal.timeout(ms) returnerer et signal, der afbryder sig selv efter ms. Timeren udløses via motorens event loop, det samme som driver setTimeout / queueMicrotask (se API: Event loop). Fetch-transporten aflæser signalet under overførslen — for standardtransporten CurlTransport via CURLOPT_PROGRESSFUNCTION — så anmodningen afbrydes hurtigt, når fristen udløber.

Løkken bruger som standard virtuel tid: den flytter sit ur frem til den næste ventende frist i stedet for at sove PHP, så et fetch(url, { signal: AbortSignal.timeout(5000) }) enten afsluttes før fristen eller afbrydes ved næste løkketakt — den får aldrig PHP til at vente. Realtidstiming kræver, at værten driver tickEventLoop() efter et vægur.

Race: den første annullering vinder

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

Videregivelse gennem lagene

Pak dit eget asynkrone arbejde ind i en signalbevidst løkke:

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

Fetch-kaldet arver signalet direkte; den manuelle if (signal.aborted) dækker mellemrummene mellem anmodninger, hvor sideløkken ellers ville fortsætte.

Lytning efter abort-hændelsen

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

signal.reason sættes, før lytterne kaldes.

Signal i en brugerdefineret fetch-transport

Hvis du udskifter transporten via setFetchTransport(), sendes signalet til din callable som en JsObject. Respektér det ved at aflæse det:

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

Hvis du kaster TransportException med kind: 'aborted', afviser JS-sidens fetch med AbortError DOMException — det samme som hvis specifikationens abort-løkke var blevet kørt.

For backends med indbygget annullering (Guzzles on_progress, Symfonys ResponseInterface::cancel) skal du tilkoble den samme aflæs-og-kast-struktur.

Mønstre, der endnu ikke virker

  • signal.addEventListener('abort', () => fetch(cleanupUrl)) — lytterne kaldes synkront; oprydnings-fetch-kaldet starter, men bliver ikke færdigt under abort-dispatchen. Planlæg det via den omgivende await.

setTimeout(() => ctrl.abort(), N) virker (timerfamilien følger med og er dækket af WPT), men AbortSignal.timeout(N) er kortere og udtrykker hensigten tydeligere, når du blot skal bruge et signal, der afbryder efter en frist.

Se også

  • Fetch-transport — komplet grænseflade for anmodning/svar/signal til brugerdefinerede transporter.
  • Web-API'er (WPT) — beståelsesprocenten for AbortController/AbortSignal i WPT.