Vzorci za AbortController
AbortController in AbortSignal sta popolni implementaciji WHATWG v Phasis. Na voljo sta tudi standardna pomočnika za sestavljanje (AbortSignal.any, AbortSignal.timeout). Ta stran obravnava najpogostejše vzorce in širjenje preklica čez mejo PHP.
Preklic zahteve fetch
const ctrl = new AbortController();
const p = fetch(url, { signal: ctrl.signal });
ctrl.abort(); // p rejects with AbortError DOMException
Razlog je privzeto AbortError, lahko pa navedete svojega:
ctrl.abort(new Error('user cancelled'));
fetch zavrne z vrednostjo, ki ste jo podali.
Časovna omejitev
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
AbortSignal.timeout(ms) vrne signal, ki se sam prekine po ms. Časovnik se sproži prek zankovnega izvajanja dogodkov pogona, istega, ki poganja setTimeout / queueMicrotask (glejte API: Zanka dogodkov). Prenosni mehanizem za pridobivanje podatkov med prenosom preverja signal — pri privzetem CurlTransport prek CURLOPT_PROGRESSFUNCTION — zato se zahteva prekine takoj, ko poteče rok.
Zanka je privzeto v navideznem času: uro pomakne na naslednji čakajoči rok, namesto da bi spala PHP, zato se fetch(url, { signal: AbortSignal.timeout(5000) }) bodisi zaključi pred rokom bodisi prekine ob naslednjem koraku zanke — PHP nikoli ne pusti čakati. Za izvajanje v realnem času mora gostitelj poganjati tickEventLoop() glede na stensko uro.
Dirka: prvo preklicanje zmaga
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
Razširjanje skozi plasti
Zanko, ki upošteva signal, uporabite za ovijanje lastnega asinhronega dela:
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;
}
Pridobivanje neposredno podeduje signal; ročni if (signal.aborted) pokrije vrzeli med zahtevami, ko bi se sicer zanka strani še naprej izvajala.
Poslušanje dogodka prekinitve
ctrl.signal.addEventListener('abort', () => {
releaseDatabaseLock();
cancelDownstreamWork();
});
signal.reason je nastavljen, preden se sprožijo poslušalci.
Signal v prenosu fetch po meri
Če zamenjate prenos prek setFetchTransport(), je signal vaši klicni funkciji posredovan kot JsObject. Upoštevajte ga s preverjanjem:
$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',
);
}
// ...
});
Če sprožite TransportException z kind: 'aborted', bo JS-stranski fetch zavrnjen z AbortError DOMException — enako, kot če bi se izvedla zanka za prekinitev iz specifikacije.
Pri zalednih sistemih z vgrajenim preklicem (Guzzle: on_progress, Symfony: ResponseInterface::cancel) uporabite enak vzorec preverjanja in sprožitve napake.
Vzorci, ki še ne delujejo
signal.addEventListener('abort', () => fetch(cleanupUrl))— poslušalci se sprožijo sinhrono; čiščenje začne zahtevo fetch, vendar se ta ne zaključi med pošiljanjem dogodka prekinitve. Načrtujte jo prek okoliškegaawait.
setTimeout(() => ctrl.abort(), N) deluje (družina časovnikov je vključena in pokrita s testi WPT), vendar je AbortSignal.timeout(N) krajši in bolje izraža namen, kadar želite le signal za prekinitev po preteku roka.
Glejte tudi
- Prenos Fetch — celoten vmesnik zahtev/odgovorov/signalov za prenose po meri.
- Spletni API-ji (WPT) — uspešnost testov WPT za AbortController/AbortSignal.