AbortController
Cancel stale work by hand, let timeouts cancel the rest, and always retry with a fresh signal.
One run shows manual aborts and a 30-second auto-timeout
Start the simulation, cancel any request manually if you want, or leave the export request alone and it will abort itself at 30 seconds.
// live controllers appear here// press "Start Demo" to populate the controller log
Invoice OCR
doc-41
PDF Chunking
doc-43
Vendor Export
exp-77
How to build it after the demo
The key retry rule is simple: abort the old attempt, then retry with a completely new controller.
async function fetchWithRetry(url: string) {
for (let attempt = 1; attempt <= 3; attempt++) {
const controller = new AbortController()
const timeoutId = setTimeout(() => {
controller.abort()
}, 5000)
try {
const response = await fetch(url, {
signal: controller.signal,
})
clearTimeout(timeoutId)
return await response.json()
} catch (error) {
clearTimeout(timeoutId)
if (attempt === 3) throw error
// important: next attempt gets a new controller
await delay(attempt * 400)
}
}
}Fresh Signal Per Retry
Never reuse an aborted signal. Every retry attempt must allocate a brand new controller.
AbortError Is Normal
Treat cancellation as control flow. It should usually skip alerts and silently exit the current path.
Combine Sources
You can combine user cancel, route changes, and timeouts into one signal so downstream code handles only one abort path.