AbortController
Pozwala przerwać operację asynchroniczną, np. zapytanie fetch(), i odpiąć wiele obsług zdarzeń naraz jednym wywołaniem abort().
- Zwraca
- Nowy obiekt
AbortControllerz właściwościąsignal.
Przykład
#const controller = new AbortController()
const { signal } = controller
signal.addEventListener('abort', () => console.log('Przerwano:', signal.reason.name))
const request = fetch('data:application/json,{"courses":12}', { signal })
controller.abort()
try {
await request
} catch (error) {
console.log('fetch odrzucony:', error.name)
}
console.log(signal.aborted)Zdarzenie abort pojawia się od razu w trakcie controller.abort(). Obietnica zapytania zostaje odrzucona błędem AbortError, który łapie blok catch.
Definicja i zastosowanie
#AbortController składa się z dwóch części: kontrolera z metodą abort() i sygnału (controller.signal), który przekazujesz do operacji, np. fetch(url, { signal }). Wywołanie abort() informuje wszystkie operacje z tym sygnałem, że mają się zakończyć. Przerwane zapytanie fetch() odrzuca obietnicę błędem o nazwie AbortError.
Sygnał ma właściwość aborted (czy przerwano), reason (powód przerwania: domyślnie błąd AbortError, ale do abort() możesz przekazać własny) i zgłasza zdarzenie abort. Metoda signal.throwIfAborted() rzuca powód przerwania, jeśli sygnał jest już przerwany, co przydaje się w dłuższych funkcjach. Jeden kontroler służy tylko raz: przerwany sygnał zostaje przerwany na zawsze, więc do kolejnej operacji utwórz nowy kontroler.
Ten sam sygnał przekażesz też do addEventListener() w opcji signal. Po abort() wszystkie takie obsługi zostaną odpięte, bez pamiętania każdej funkcji osobno. Gotowe sygnały tworzą też metody statyczne: AbortSignal.timeout(ms) przerywa operację po upływie czasu (z błędem TimeoutError), a nowsza AbortSignal.any([...]) łączy kilka sygnałów w jeden.
Składnia
#const controller = new AbortController()
controller.signal
controller.abort()
controller.abort(reason)
signal.aborted
signal.reason
signal.throwIfAborted()
AbortSignal.timeout(ms)Parametry
#reason
opcjonalny, dowolna wartość
Powód przerwania przekazany doabort(). Trafi dosignal.reasoni do błędu odrzucającego obietnicę. Domyślnie to błądDOMExceptiono nazwieAbortError.
Więcej przykładów
#let controller
async function search(query) {
controller?.abort()
controller = new AbortController()
try {
const response = await fetch(`data:application/json,{"query":"${query}"}`, {
signal: controller.signal
})
const data = await response.json()
console.log('Wyniki dla:', data.query)
} catch (error) {
if (error.name !== 'AbortError') throw error
console.log('Anulowano:', query)
}
}
search('ja')
search('jav')
search('java')Każde nowe wyszukiwanie przerywa poprzednie zapytanie, więc wynik dostajemy tylko dla ostatniego tekstu. Tak działa wyszukiwarka, która szuka już w trakcie pisania.
const button = document.querySelector('#start')
const controller = new AbortController()
const { signal } = controller
button.addEventListener('click', () => console.log('Start quizu'), { signal })
button.addEventListener('click', () => console.log('Licznik czasu włączony'), { signal })
document.addEventListener('keydown', () => console.log('Wciśnięto klawisz'), { signal })
button.click()
controller.abort()
button.click()
console.log('Obsługi odpięte:', signal.aborted)Po controller.abort() drugie kliknięcie niczego nie wypisuje. Wszystkie trzy obsługi zostały odpięte jednym wywołaniem.
function wait(ms, signal) {
return new Promise((resolve, reject) => {
const timer = setTimeout(resolve, ms)
signal.addEventListener(
'abort',
() => {
clearTimeout(timer)
reject(signal.reason)
},
{ once: true }
)
})
}
const controller = new AbortController()
setTimeout(() => controller.abort(new Error('Przekroczono limit czasu na odpowiedź')), 200)
try {
await wait(1000, controller.signal)
console.log('Odpowiedź na czas')
} catch (error) {
console.log(error.message)
}Funkcja wait() nasłuchuje zdarzenia abort na sygnale i odrzuca obietnicę z powodem przekazanym do abort(). W ten sam sposób możesz dodać przerywanie do własnych operacji asynchronicznych.
Obsługa przeglądarek
#Szeroko dostępne · od 2019 roku
Działa we wszystkich nowoczesnych przeglądarkach, także na telefonach. Możesz używać bez obaw.
- Chrome
- Edge
- Firefox
- Safari
Dobre praktyki
#- Do każdej nowej operacji twórz nowy
AbortController. Przerwany sygnał zostaje przerwany na zawsze i od razu przerwie każde kolejne zapytanie. - W bloku
catchodróżniaj przerwanie od prawdziwego błędu przezerror.name === "AbortError". Anulowanie przez użytkownika zwykle nie wymaga komunikatu o błędzie. - Do prostego limitu czasu wystarczy
fetch(url, { signal: AbortSignal.timeout(5000) }), bez tworzenia kontrolera.
Powiązane hasła
#- fetch()Wysyła zapytanie HTTP i zwraca obietnicę z odpowiedzią serwera (obiektem Response).
- EventTarget.removeEventListener()Odpina funkcję obsługi zdarzenia dodaną wcześniej przez addEventListener(). Wymaga tej samej funkcji i tego samego typu zdarzenia.
- EventTarget.addEventListener()Rejestruje funkcję wywoływaną za każdym razem, gdy na elemencie wystąpi zdarzenie, np. kliknięcie.
- PromiseObiekt reprezentujący wynik operacji, który pojawi się w przyszłości: wartość przy sukcesie albo błąd.
- try...catchPrzechwytuje błędy rzucone w bloku try i obsługuje je w catch. Opcjonalny finally wykona się zawsze.
- async functionDeklaruje funkcję asynchroniczną, która zawsze zwraca obietnicę i może używać w środku słowa await.
Widzisz błąd albo brakuje przykładu? Napisz do nas.