AbortController utilities — timeout, race, linked signals
npm install @philiprehberger/abort-kitAbortController utilities — timeout, race, linked signals
npm install @philiprehberger/abort-kit
import {
withTimeout,
withTimeoutCancellable,
anySignal,
linkedSignal,
onAbort,
signalChain,
isAbortError,
throwIfAborted,
} from '@philiprehberger/abort-kit';
// Abort after 5 seconds
const { signal, cleanup } = withTimeout({ ms: 5000 });
fetch('/api/data', { signal }).finally(cleanup);
withTimeoutCancellable exposes an explicit cancel() that clears the pending
timer without aborting the signal — ideal when callers consume the signal
quickly and want to release the timer deterministically.
import { withTimeoutCancellable } from '@philiprehberger/abort-kit';
const { signal, cancel } = withTimeoutCancellable({ ms: 5000 });
try {
const response = await fetch('/api/data', { signal });
// Request finished before the timeout — release the timer immediately.
cancel();
return response;
} catch (err) {
cancel();
throw err;
}
import { anySignal, linkedSignal } from '@philiprehberger/abort-kit';
// Combine multiple signals — abort when any fires
const a = new AbortController();
const b = new AbortController();
const combined = anySignal([a.signal, b.signal]);
// Create a child signal linked to a parent
const parent = new AbortController();
const child = linkedSignal(parent.signal);
parent.abort();
// child.signal is now aborted with the parent's reason
import { onAbort } from '@philiprehberger/abort-kit';
const controller = new AbortController();
const removeListener = onAbort(controller.signal, (reason) => {
console.log('Aborted:', reason);
});
// Later — remove the listener.
removeListener();
signalChain reports a signal's chain depth and current abort state. Useful
for debugging deeply linked signals built up by middleware or HTTP clients.
import { anySignal, linkedSignal, signalChain } from '@philiprehberger/abort-kit';
const root = new AbortController();
const mid = linkedSignal(root.signal);
const leaf = linkedSignal(mid.signal);
signalChain(leaf.signal);
// => { depth: 2, isAborted: false, reason: undefined }
root.abort('shutdown');
signalChain(leaf.signal);
// => { depth: 2, isAborted: true, reason: 'shutdown' }
import { isAbortError, throwIfAborted } from '@philiprehberger/abort-kit';
try {
await fetch('/api/data', { signal });
} catch (err) {
if (isAbortError(err)) {
console.log('Request was aborted');
}
}
// Bail out early if already aborted
throwIfAborted(signal);
| Export | Description |
|---|---|
withTimeout(options) | Create an AbortSignal that aborts after a timeout, optionally combined with an existing signal. Returns { signal, cleanup }. |
withTimeoutCancellable(options) | Like withTimeout but returns { signal, cancel } where cancel() clears the pending timer without aborting the signal. |
anySignal(signals) | Combine multiple AbortSignals into one that aborts when any input signal aborts. |
linkedSignal(parent) | Create a child AbortController that auto-aborts when the parent signal aborts. Returns { controller, signal, cleanup }. |
onAbort(signal, callback) | Attach a callback that fires on abort (immediately if already aborted). Returns a cleanup function. |
signalChain(signal) | Inspect a signal's chain — returns { depth, isAborted, reason }. Depth reflects parents tracked by linkedSignal/anySignal/withTimeout*. |
isAbortError(error) | Type guard — returns true if the error is a DOMException with name "AbortError". |
throwIfAborted(signal) | Throw the signal's reason if already aborted. |
npm install
npm run build
npm run typecheck
npm test
If you find this project useful: