-
-
Notifications
You must be signed in to change notification settings - Fork 36
/
index.js
70 lines (55 loc) · 1.69 KB
/
index.js
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
// From https://github.com/sindresorhus/random-int/blob/c37741b56f76b9160b0b63dae4e9c64875128146/index.js#L13-L15
const randomInteger = (minimum, maximum) => Math.floor((Math.random() * (maximum - minimum + 1)) + minimum);
const createAbortError = () => {
const error = new Error('Delay aborted');
error.name = 'AbortError';
return error;
};
const clearMethods = new WeakMap();
export function createDelay({clearTimeout: defaultClear, setTimeout: defaultSet} = {}) {
// We cannot use `async` here as we need the promise identity.
return (milliseconds, {value, signal} = {}) => {
// TODO: Use `signal?.throwIfAborted()` when targeting Node.js 18.
if (signal?.aborted) {
return Promise.reject(createAbortError());
}
let timeoutId;
let settle;
let rejectFunction;
const clear = defaultClear ?? clearTimeout;
const signalListener = () => {
clear(timeoutId);
rejectFunction(createAbortError());
};
const cleanup = () => {
if (signal) {
signal.removeEventListener('abort', signalListener);
}
};
const delayPromise = new Promise((resolve, reject) => {
settle = () => {
cleanup();
resolve(value);
};
rejectFunction = reject;
timeoutId = (defaultSet ?? setTimeout)(settle, milliseconds);
});
if (signal) {
signal.addEventListener('abort', signalListener, {once: true});
}
clearMethods.set(delayPromise, () => {
clear(timeoutId);
timeoutId = null;
settle();
});
return delayPromise;
};
}
const delay = createDelay();
export default delay;
export async function rangeDelay(minimum, maximum, options = {}) {
return delay(randomInteger(minimum, maximum), options);
}
export function clearDelay(promise) {
clearMethods.get(promise)?.();
}