generated from shgysk8zer0/npm-template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtempo.js
More file actions
113 lines (106 loc) · 4.12 KB
/
tempo.js
File metadata and controls
113 lines (106 loc) · 4.12 KB
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* Debounces a function using scheduler.postTask().
*
* @param {Function} callback The function to debounce.
* @param {object} [options] Debounce options.
* @param {number} [options.delay] The debounce delay in milliseconds.
* @param {"user-blocking"|"user-visible"|"background"} [options.priority] The task priority ('user-blocking', 'user-visible', 'background').
* @param {AbortSignal} [options.signal] An AbortSignal to cancel the debounced call.
* @param {any} [options.thisArg=null] The 'this' context for the callback.
* @returns {Function} A debounced version of the input function.
* @throws {TypeError} If the callback is not a function.
* @throws {Error} If the provided AbortSignal is already aborted.
*/
export function debounce(callback, {
delay,
priority,
signal,
thisArg = null,
} = {}) {
if (typeof callback !== 'function') {
throw new TypeError('Callback must be a function.');
} else if (signal instanceof AbortSignal && signal.aborted) {
throw signal.reason;
} else if (signal instanceof AbortSignal) {
let controller;
return async (...args) => {
if (! signal.aborted) {
if (controller instanceof AbortController && ! controller.signal.aborted) {
controller.abort();
}
controller = new AbortController();
return await scheduler.postTask(() => callback.apply(thisArg, args), {
delay,
priority,
signal: AbortSignal.any([signal, controller.signal]),
}).catch(() => null);
}
};
} else {
let controller;
return async (...args) => {
if (controller instanceof AbortController && ! controller.signal.aborted) {
controller.abort();
}
controller = new AbortController();
return await scheduler.postTask(() => callback.apply(thisArg, args), {
delay,
priority,
signal: controller.signal,
}).catch(() => null);
};
}
}
/**
* Throttle a function using scheduler.postTask().
*
* @param {Function} callback The function to throttle.
* @param {object} [options] Throttle options.
* @param {string} [options.lockName=crypto.randomUUID()] The name for the lock, defaulting to a random UUID.
* @param {number} [options.delay] The delay in milliseconds.
* @param {"user-blocking"|"user-visible"|"background"} [options.priority] The task priority ('user-blocking', 'user-visible', 'background').
* @param {boolean} [options.ifAvailable=true] If true, the lock request fails immediately if the lock is not available. If false, the request waits.
* @param {boolean} [options.steal=false] If true, the lock request can "steal" the lock from waiting requests. If false, the request waits in the queue.
* @param {"shared"|"exclusive"} [options.mode="exclusive"] The lock mode ('exclusive'). Only 'exclusive' is supported for throttling.
* @param {any} [options.thisArg=null] The 'this' context for the callback.
* @param {AbortSignal} [options.signal] An AbortSignal to cancel the throttled call.
* @returns {Function} A throttled version of the input function.
* @throws {TypeError} If the callback is not a function or if both `steal` and `ifAvailable` are both true.
* @throws {Error} If the provided AbortSignal is already aborted.
*/
export function throttle(callback, {
lockName = crypto.randomUUID(),
delay,
priority,
ifAvailable = true,
steal = false,
mode = 'exclusive',
signal,
thisArg = null,
} = {}) {
if (typeof callback !== 'function') {
throw new TypeError('Callback must be a function.');
} else if (steal && ifAvailable) {
throw new TypeError('Cannot set both `steal` and `ifAvailable`.');
} else if (signal instanceof AbortSignal && signal.aborted) {
throw signal.reason;
} else {
return async (...args) => {
const hasSignal = signal instanceof AbortSignal;
await navigator.locks.request(
lockName,
{ mode, ifAvailable, steal },
async (lock) => {
// Cannot use both `signal` and `ifAvailable` together
if (lock instanceof Lock && ! (hasSignal && signal.aborted)) {
const result = await scheduler.postTask(() => callback.apply(thisArg, args), { priority, signal });
await new Promise(resolve => setTimeout(resolve, delay));
return result;
} else {
return null;
}
}
);
};
}
}