typescript / expert
Snippet
Cancellation komponieren mit AbortSignal.any und timeout
AbortSignal.timeout erzeugt ein Signal, das nach der angegebenen Frist automatisch abbricht; AbortSignal.any führt mehrere Signale zusammen, sodass das erste Abort gewinnt und das kombinierte Signal dessen Grund trägt. Mit try/catch übersetzt der Helper jeden Abbruch – ob Timeout oder externer Cancel – in einen einzigen typisierten Fehler, während echte Task-Fehler unverändert weiterfliegen. Der Task bekommt nur das kombinierte Signal und bleibt frei von Cancellation-Sanitärzeug.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class CancelledError extends Error {constructor(public readonly reason: unknown) {super('operation cancelled');this.name = 'CancelledError';}}async function withDeadline<T>(task: (signal: AbortSignal) => Promise<T>,ms: number,external?: AbortSignal,): Promise<T> {const timer = AbortSignal.timeout(ms);const signals = external ? [timer, external] : [timer];const combined = AbortSignal.any(signals);try {return await task(combined);} catch (err) {if (combined.aborted) throw new CancelledError(combined.reason);throw err;}}const userAbort = new AbortController();await withDeadline((s) => doWork(s), 500, userAbort.signal);
Erklärung
1
const timer = AbortSignal.timeout(ms);
Die statische Factory liefert ein selbst-abbrechendes Signal – ohne Controller oder setTimeout.
2
const combined = AbortSignal.any(signals);
Bricht ab, sobald irgendein Eingangssignal abbricht, und gibt dessen Grund weiter.
3
if (combined.aborted) throw new CancelledError(combined.reason);
Trennt einen abbruchbedingten Reject von einem echten Task-Fehler.
4
return await task(combined);
Der Task sieht nur ein Signal und kennt die Zusammensetzung der Cancellation nicht.
5
await withDeadline((s) => doWork(s), 500, userAbort.signal);
Der Aufrufer kombiniert seinen AbortController mit dem internen Timeout des Helpers.