Rules Hub
Coding Rules Library
Merge cancellation signals with AbortSignal.any
Combine multiple cancellation sources (timeouts, user actions) into a single signal using AbortSignal.any to ensure robust resource cleanup.
Bad example
| 1 | async function fetchWithTimeout(url: string, parentSignal?: AbortSignal) { |
| 2 | const timeoutCtrl = new AbortController(); |
| 3 | const id = setTimeout(() => timeoutCtrl.abort(), 5000); |
| 4 |
|
| 5 | try { |
| 6 | // BAD: The fetch only respects the timeout. |
| 7 | // If the user navigates away (triggering parentSignal), the request continues running. |
| 8 | const res = await fetch(url, { signal: timeoutCtrl.signal }); |
| 9 | return res.json(); |
| 10 | } finally { |
| 11 | clearTimeout(id); |
| 12 | } |
| 13 | } |
Explanation (EN)
By using only the local `timeoutCtrl.signal`, the function ignores the `parentSignal` passed from the caller (e.g., a component unmounting or user navigation). This leads to 'zombie' requests that continue even after they are no longer needed.
Objašnjenje (HR)
Korištenjem samo lokalnog `timeoutCtrl.signal` funkcija ignorira `parentSignal` koji je proslijedio pozivatelj (npr. prilikom demontaže komponente ili navigacije). To dovodi do 'zombie' zahtjeva koji se nastavljaju izvršavati čak i nakon što više nisu potrebni.
Good example
| 1 | import { mergeAbortSignals } from '@/lib/utils'; // Wrapper for AbortSignal.any |
| 2 |
|
| 3 | async function fetchWithTimeout(url: string, parentSignal?: AbortSignal) { |
| 4 | const timeoutCtrl = new AbortController(); |
| 5 | const id = setTimeout(() => timeoutCtrl.abort(), 5000); |
| 6 |
|
| 7 | // GOOD: Combines both signals. The request aborts if EITHER the timeout fires OR parentSignal aborts. |
| 8 | // Uses AbortSignal.any() under the hood. |
| 9 | const combinedSignal = mergeAbortSignals(parentSignal, timeoutCtrl.signal); |
| 10 |
|
| 11 | try { |
| 12 | const res = await fetch(url, { signal: combinedSignal }); |
| 13 | return res.json(); |
| 14 | } finally { |
| 15 | clearTimeout(id); |
| 16 | } |
| 17 | } |
Explanation (EN)
All potential cancellation sources are merged into a single `AbortSignal`. Using `AbortSignal.any` (or a robust polyfill) ensures that the operation terminates immediately when the first signal aborts, preventing resource wastage and memory leaks.
Objašnjenje (HR)
Svi potencijalni izvori otkazivanja spojeni su u jedan `AbortSignal`. Korištenje `AbortSignal.any` (ili robusnog polyfilla) osigurava da se operacija odmah prekine čim se prvi signal aktivira, čime se sprječava rasipanje resursa i curenje memorije.
Notes (EN)
Modern browsers and Node.js (v20+) support `AbortSignal.any()` natively. For older environments, use a utility function that gracefully falls back to a manual `AbortController` implementation with proper event cleanup.
Bilješke (HR)
Moderni preglednici i Node.js (v20+) nativno podržavaju `AbortSignal.any()`. Za starija okruženja koristite pomoćnu funkciju koja se elegantno vraća na ručnu implementaciju `AbortController` uz ispravno čišćenje događaja.