-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
87 lines (77 loc) · 3.61 KB
/
index.ts
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* A helper class to create an {@link !AbortSignal AbortSignal} which will abort when any of the signals passed to its constructor do.
*/
export class AggregateSignal {
/**
* The aggregate {@link !AbortSignal AbortSignal}.
*
* @remarks
* If only a single valid {@link !AbortSignal AbortSignal} was passed, it will be that signal.
* If any of the signals passed was already aborted, it will be the first match in the array.
* If no valid {@link !AbortSignal AbortSignals} were passed,
* it will be {@link !undefined `undefined`}.
*/
readonly signal?: AbortSignal;
/** The first {@link !AbortSignal AbortSignal} of those passed in to have aborted. */
abortedSignal?: AbortSignal;
/**
* Initializes a new {@link AggregateSignal}.
* @param {(AbortSignal | undefined)[]} abortSignals The {@link !AbortSignal AbortSignals} to aggregate.
*/
constructor(...abortSignals: (AbortSignal | undefined)[]) {
const signals = abortSignals.filter(isAbortSignal);
if (signals.length === 1) {
this.abortedSignal = this.signal = signals[0];
} else if (signals.some((s) => s.aborted)) {
this.abortedSignal = this.signal = signals.filter(
(s) => s.aborted
)[0];
} else if (signals.length > 1) {
const ac = new AbortController();
this.signal = ac.signal;
for (const signal of signals) {
const listener = () => {
for (const signal of signals) {
signal.removeEventListener('abort', listener);
}
this.abortedSignal = signal;
ac.abort();
};
signal.addEventListener('abort', listener);
}
}
}
}
/**
* A helper class to create an {@link !AbortSignal AbortSignal} based on {@link !setTimeout setTimeout}.
*/
export class TimeoutSignal {
/** The underlying {@link !AbortSignal AbortSignal}. */
public readonly signal?: AbortSignal;
/** If defined, the {@link https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#return_value timeoutID} of a timer which will signal abortion. */
public readonly timeout?: ReturnType<typeof setTimeout>;
/**
* Initializes a new {@link TimeoutSignal}.
* @param {number} [timeout] The number of milliseconds after which the {@link TimeoutSignal.signal signal} should be aborted.
* {@link !undefined `undefined`}, {@link !Infinity infinite} or {@link !NaN `NaN`} values will result in {@link TimeoutSignal.signal signal}
* being {@link !undefined `undefined`}.
* Finite values will be clamped between `0` and {@link !Number.MAX_SAFE_INTEGER `Number.MAX_SAFE_INTEGER`} inclusive.
*/
constructor(timeout?: number) {
if (timeout && isFinite(timeout) && !isNaN(timeout)) {
timeout = Math.min(Math.max(timeout, 0), Number.MAX_SAFE_INTEGER); // clamp the timeout to a sensible range
const ac = new AbortController();
this.signal = ac.signal; // wrap the AbortController's signal
this.timeout = setTimeout(() => ac.abort(), timeout); // abort after the given number of milliseconds
}
}
}
/**
* Type guard for determining whether a given object is an {@link !AbortSignal AbortSignal} instance.
* @param {unknown} object The object.
* @returns {object is AbortSignal} `true` if the object is determined to be an {@link !AbortSignal AbortSignal},
* otherwise `false`.
*/
export function isAbortSignal(object: unknown): object is AbortSignal {
return object instanceof AbortSignal;
}