typescriptadvanced
AbortController for Cancellation
Cancel async operations with AbortController: fetch requests, timers, streams, and custom operations.
typescriptPress ⌘/Ctrl + Shift + C to copy
// Cancellable fetch
async function fetchWithTimeout(url: string, timeoutMs: number): Promise<Response> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
return response;
} finally {
clearTimeout(timeout);
}
}
// Cancellable delay
function cancellableDelay(ms: number, signal?: AbortSignal): Promise<void> {
return new Promise((resolve, reject) => {
if (signal?.aborted) {
reject(signal.reason);
return;
}
const timer = setTimeout(resolve, ms);
signal?.addEventListener('abort', () => {
clearTimeout(timer);
reject(signal.reason);
}, { once: true });
});
}
// Cancellable async iterator
async function* pollEndpoint(
url: string,
intervalMs: number,
signal: AbortSignal
): AsyncGenerator<unknown> {
while (!signal.aborted) {
try {
const res = await fetch(url, { signal });
yield await res.json();
await cancellableDelay(intervalMs, signal);
} catch (e) {
if (signal.aborted) return;
throw e;
}
}
}
// Linked controllers (child cancellation)
function linkedController(parent: AbortSignal): AbortController {
const child = new AbortController();
if (parent.aborted) {
child.abort(parent.reason);
} else {
parent.addEventListener('abort', () => {
child.abort(parent.reason);
}, { once: true });
}
return child;
}
// Race multiple operations with cancellation
async function raceWithCancel<T>(
operations: ((signal: AbortSignal) => Promise<T>)[]
): Promise<T> {
const controller = new AbortController();
try {
return await Promise.race(
operations.map((op) => op(controller.signal))
);
} finally {
controller.abort(); // cancel losers
}
}
// Custom cancellable operation
class CancellableTask<T> {
private controller = new AbortController();
private promise: Promise<T>;
constructor(executor: (signal: AbortSignal) => Promise<T>) {
this.promise = executor(this.controller.signal);
}
cancel(reason?: string) {
this.controller.abort(reason ?? 'Cancelled');
}
get result(): Promise<T> {
return this.promise;
}
}
// Usage
try {
const response = await fetchWithTimeout('https://httpbin.org/delay/1', 5000);
console.log('Status:', response.status);
} catch (e) {
if ((e as Error).name === 'AbortError') {
console.log('Request timed out');
}
}
// Cancellable task
const task = new CancellableTask(async (signal) => {
await cancellableDelay(1000, signal);
return 'done';
});
setTimeout(() => task.cancel('Too slow'), 500);
try {
await task.result;
} catch (e) {
console.log('Task cancelled:', (e as Error).message);
}Use Cases
- Request timeout handling
- Graceful operation cancellation
- Race conditions with cleanup
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptadvanced
Node.js In-Memory Task Queue
A simple in-memory task queue with concurrency control, retries, and priority support.
Best for: Rate-limited API call processing
#nodejs#queue
typescriptintermediate
Event Loop and Timers
Understand Node.js event loop phases with setTimeout, setInterval, setImmediate, and process.nextTick.
Best for: Understanding async execution order
#nodejs#event-loop
typescriptintermediate
Promise Concurrency Patterns
Master Promise.all, allSettled, race, any — parallel execution with error handling and timeouts.
Best for: Parallel API calls with error handling
#nodejs#promise
typescriptadvanced
Async Iterators for Stream Processing
Process streams and async data sources with for-await-of loops and custom async iterators in Node.js.
Best for: Paginated API data consumption
#nodejs#async-iterator