AbortController-patronen
AbortController en AbortSignal zijn volledige WHATWG-implementaties in Phasis. Ook de standaardcompositiehelpers (AbortSignal.any, AbortSignal.timeout) worden meegeleverd. Op deze pagina staan de patronen die je in de praktijk gebruikt en hoe annulering zich over de grens van PHP verspreidt.
Een fetch annuleren
const ctrl = new AbortController();
const p = fetch(url, { signal: ctrl.signal });
ctrl.abort(); // p rejects with AbortError DOMException
De reden is standaard AbortError, maar je kunt er zelf een opgeven:
ctrl.abort(new Error('user cancelled'));
fetch wordt afgewezen met de waarde die je hebt doorgegeven.
Time-out
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
AbortSignal.timeout(ms) geeft een signaal terug dat zichzelf na ms afbreekt. De timer wordt geactiveerd via de eventloop van de engine, dezelfde die setTimeout / queueMicrotask aanstuurt (zie API: Eventloop). Het fetchtransport controleert het signaal tijdens de overdracht — voor de standaard-CurlTransport via CURLOPT_PROGRESSFUNCTION — zodat het verzoek direct wordt afgebroken zodra de deadline verstrijkt.
De loop gebruikt standaard virtuele tijd: de klok wordt vooruitgezet naar de volgende openstaande deadline in plaats van dat de engine PHP slaapt. Daardoor is een fetch(url, { signal: AbortSignal.timeout(5000) }) óf klaar voordat de deadline verstrijkt, óf wordt deze bij de volgende looptik afgebroken — PHP hoeft nooit te wachten. Voor timing in echte tijd moet de host tickEventLoop() aansturen met een wandklok.
Race: de eerste annulering wint
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
Propagatie door lagen heen
Omwikkel je eigen asynchrone werk met een signaalgevoelige loop:
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;
}
De fetch erft het signaal rechtstreeks; de handmatige if (signal.aborted) dekt de gaten tussen verzoeken waarin de paginaloop anders zou blijven draaien.
Luisteren naar de abort-gebeurtenis
ctrl.signal.addEventListener('abort', () => {
releaseDatabaseLock();
cancelDownstreamWork();
});
signal.reason wordt ingesteld voordat listeners worden aangeroepen.
Signaal in een aangepast fetchtransport
Als je het transport vervangt via setFetchTransport(), wordt het signaal als JsObject doorgegeven aan je callable. Houd er rekening mee door het te controleren:
$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',
);
}
// ...
});
Door TransportException te gooien met kind: 'aborted' wordt JS-zijde fetch afgewezen met AbortError DOMException — net alsof de abort-loop uit de specificatie was uitgevoerd.
Gebruik voor backends met native annulering (Guzzle's on_progress, Symfony's ResponseInterface::cancel) dezelfde aanpak: controleer het signaal en gooi een fout.
Patronen die nog niet werken
signal.addEventListener('abort', () => fetch(cleanupUrl))— listeners worden synchroon aangeroepen. De opschoon-fetch start, maar wordt niet voltooid tijdens de abort-dispatch. Plan deze via de omringendeawait.
setTimeout(() => ctrl.abort(), N) werkt (de timerfamilie wordt meegeleverd en is gedekt door WPT), maar AbortSignal.timeout(N) is korter en geeft de bedoeling beter weer als je alleen een signaal nodig hebt dat na een deadline wordt afgebroken.
Zie ook
- Fetchtransport — volledige request-, response- en signaalinterface voor aangepaste transports.
- Web-API's (WPT) — slaagpercentage voor AbortController/AbortSignal in WPT.