Jak víme, metoda fetch vrací příslib. A JavaScript obecně nemá žádný koncept „odvolání“ příslibu. Jak tedy můžeme zastavit probíhající fetch? Například pokud uživatelské akce na naší stránce naznačují, že tento fetch již nebude zapotřebí.
K tomuto účelu slouží speciální zabudovaný objekt: AbortController. Můžeme jej použít nejen k zastavení fetch, ale i jiných asynchronních úloh.
Jeho použití je velmi přímočaré:
Objekt AbortController
Vytvoříme controller:
let controller = new AbortController();
Controller je extrémně jednoduchý objekt.
- Obsahuje jedinou metodu
abort() - a jedinou vlastnost
signal, která umožňuje na něm nastavovat posluchače událostí.
Když je abort() volána:
controller.signalvyvolá událost"abort".- Vlastnost
controller.signal.abortedse nastaví natrue.
Obecně máme v tomto procesu dvě části:
- Ta, která provádí zrušitelnou operaci, nastaví posluchače událostí na
controller.signal. - Ta, která ruší: když je potřeba, volá
controller.abort().
Následuje úplný příklad (zatím bez fetch):
let controller = new AbortController();
let signal = controller.signal;
// Strana, která provádí zrušitelnou operaci,
// obdrží objekt „signal“
// a nastaví posluchače, který se spustí, když bude volána controller.abort()
signal.addEventListener('abort', () => alert("zastaveno!"));
// Druhá strana, která provede zastavení (kdykoli později):
controller.abort(); // zastaveno!
// Událost se spustí a signal.aborted se nastaví na true
alert(signal.aborted); // true
Jak vidíme, AbortController je jen způsob, jak předávat událost abort, když je na něm volán abort().
Stejný způsob naslouchání událostem si můžeme v našem kódu implementovat i sami, bez objektu AbortController.
Na něm však je cenné to, že metoda fetch umí s objektem AbortController pracovat. Je do ní integrován.
Použití s metodou fetch
Abychom mohli zastavit fetch, předáme vlastnost signal objektu AbortController do voleb metody fetch:
let controller = new AbortController();
fetch(url, {
signal: controller.signal
});
Metoda fetch umí s objektem AbortController pracovat. Naslouchá událostem abort na signal.
Když ji nyní chceme zastavit, zavoláme controller.abort():
controller.abort();
Jsme hotovi: fetch obdrží událost od signal a požadavek zastaví.
Když je stahování zastaveno, příslib bude zamítnut s chybou AbortError. Měli bychom ji tedy zpracovat, např. v bloku try..catch.
V následujícím příkladu se fetch zastaví po 1 sekundě:
// zastavení po 1 sekundě
let controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
try {
let odpověď = await fetch('/article/fetch-abort/demo/hang', {
signal: controller.signal
});
} catch(err) {
if (err.name == 'AbortError') { // ošetříme abort()
alert("Zastaveno!");
} else {
throw err;
}
}
AbortController je rozšiřitelný
AbortController je rozšiřitelný. Umožňuje zastavit více stahování najednou.
Následující nástin kódu stahuje z mnoha adres v poli poleURL současně a používá jediný controller, který všechna stahování zastaví:
let poleURL = [...]; // seznam URL, z nichž se má paralelně stahovat
let controller = new AbortController();
// pole příslibů fetch
let poleStahování = poleURL.map(url => fetch(url, {
signal: controller.signal
}));
let výsledky = await Promise.all(poleStahování);
// pokud je odkudkoli volána controller.abort(),
// všechna stahování se zastaví
Jestliže máme naše vlastní asynchronní úlohy, jiné než fetch, můžeme je všechny zastavit jediným objektem AbortController společně s metodami fetch.
Jen musíme v našich úlohách naslouchat jeho události abort:
let poleURL = [...];
let controller = new AbortController();
let našeČinnost = new Promise((splň, zamítni) => { // naše úloha
...
controller.signal.addEventListener('abort', zamítni);
});
let poleStahování = poleURL.map(url => fetch(url, { // stahování
signal: controller.signal
}));
// Čekáme na stahování a naši úlohu současně
let výsledky = await Promise.all([...poleStahování, našeČinnost]);
// pokud je odkudkoli volána controller.abort(),
// zastaví se všechna stahování i našeČinnost
Shrnutí
AbortControllerje jednoduchý objekt, který generuje událostabortna své vlastnostisignal, když je volána metodaabort()(a také nastavísignal.abortednatrue).- Metoda
fetchje s ním integrována: předáme jí vlastnostsignaljako její volbu a pak mufetchnaslouchá, je tedy možnéfetchzastavit. - Můžeme použít
AbortControllerv našem kódu. Interakce „voláníabort()“ → „nasloucháme událostiabort“ je jednoduchá a univerzální. Můžeme ji použít i bezfetch.
Komentáře
<code>, pro několik řádků je obalte značkou<pre>, pro více než 10 řádků vložte odkaz na pískoviště (plnkr, jsbin, codepen…)