@cjser/p-debounce 5.1.0-cjser.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjser/index.cjs +141 -0
- package/index.d.ts +113 -0
- package/index.js +153 -0
- package/license +9 -0
- package/package.json +80 -0
- package/readme.md +141 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// packages/@cjser/p-debounce/index.js
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
default: () => index_default
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(index_exports);
|
|
25
|
+
var pDebounce = (functionToDebounce, wait, options = {}) => {
|
|
26
|
+
if (!Number.isFinite(wait)) {
|
|
27
|
+
throw new TypeError("Expected `wait` to be a finite number");
|
|
28
|
+
}
|
|
29
|
+
let leadingValue;
|
|
30
|
+
let timeout;
|
|
31
|
+
let promiseHandlers = [];
|
|
32
|
+
const onAbort = () => {
|
|
33
|
+
var _a;
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
timeout = void 0;
|
|
36
|
+
try {
|
|
37
|
+
(_a = options.signal) == null ? void 0 : _a.throwIfAborted();
|
|
38
|
+
} catch (error) {
|
|
39
|
+
for (const { reject } of promiseHandlers) {
|
|
40
|
+
reject(error);
|
|
41
|
+
}
|
|
42
|
+
promiseHandlers = [];
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return function(...arguments_) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
var _a;
|
|
48
|
+
try {
|
|
49
|
+
(_a = options.signal) == null ? void 0 : _a.throwIfAborted();
|
|
50
|
+
} catch (error) {
|
|
51
|
+
reject(error);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const shouldCallNow = options.before && !timeout;
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
timeout = setTimeout(async () => {
|
|
57
|
+
var _a2;
|
|
58
|
+
timeout = void 0;
|
|
59
|
+
const currentHandlers = promiseHandlers;
|
|
60
|
+
promiseHandlers = [];
|
|
61
|
+
try {
|
|
62
|
+
const result = options.before ? leadingValue : await functionToDebounce.apply(this, arguments_);
|
|
63
|
+
for (const { resolve: resolveFunction } of currentHandlers) {
|
|
64
|
+
resolveFunction(result);
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
for (const { reject: rejectFunction } of currentHandlers) {
|
|
68
|
+
rejectFunction(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
leadingValue = void 0;
|
|
72
|
+
(_a2 = options.signal) == null ? void 0 : _a2.removeEventListener("abort", onAbort);
|
|
73
|
+
}, wait);
|
|
74
|
+
if (shouldCallNow) {
|
|
75
|
+
(async () => {
|
|
76
|
+
try {
|
|
77
|
+
leadingValue = await functionToDebounce.apply(this, arguments_);
|
|
78
|
+
resolve(leadingValue);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
reject(error);
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
} else {
|
|
84
|
+
promiseHandlers.push({ resolve, reject });
|
|
85
|
+
if (options.signal && promiseHandlers.length === 1) {
|
|
86
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
pDebounce.promise = (function_, options = {}) => {
|
|
93
|
+
let currentPromise;
|
|
94
|
+
let queuedCall;
|
|
95
|
+
return async function(...arguments_) {
|
|
96
|
+
if (currentPromise) {
|
|
97
|
+
if (!options.after) {
|
|
98
|
+
return currentPromise;
|
|
99
|
+
}
|
|
100
|
+
queuedCall ??= { resolvers: [] };
|
|
101
|
+
queuedCall.arguments = arguments_;
|
|
102
|
+
queuedCall.context = this;
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
queuedCall.resolvers.push({ resolve, reject });
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
currentPromise = (async () => {
|
|
108
|
+
let result;
|
|
109
|
+
let initialError;
|
|
110
|
+
try {
|
|
111
|
+
result = await function_.apply(this, arguments_);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
initialError = error;
|
|
114
|
+
}
|
|
115
|
+
while (queuedCall) {
|
|
116
|
+
const call = queuedCall;
|
|
117
|
+
queuedCall = void 0;
|
|
118
|
+
try {
|
|
119
|
+
const queuedResult = await function_.apply(call.context, call.arguments);
|
|
120
|
+
for (const { resolve } of call.resolvers) {
|
|
121
|
+
resolve(queuedResult);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
for (const { reject } of call.resolvers) {
|
|
125
|
+
reject(error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (initialError) {
|
|
130
|
+
throw initialError;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
})();
|
|
134
|
+
try {
|
|
135
|
+
return await currentPromise;
|
|
136
|
+
} finally {
|
|
137
|
+
currentPromise = void 0;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
var index_default = pDebounce;
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export type Options = {
|
|
2
|
+
/**
|
|
3
|
+
Call the `fn` on the [leading edge of the timeout](https://css-tricks.com/debouncing-throttling-explained-examples/#article-header-id-1). Meaning immediately, instead of waiting for `wait` milliseconds.
|
|
4
|
+
|
|
5
|
+
@default false
|
|
6
|
+
*/
|
|
7
|
+
readonly before?: boolean;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
An `AbortSignal` to cancel the debounced function.
|
|
11
|
+
*/
|
|
12
|
+
readonly signal?: AbortSignal;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type PromiseOptions = {
|
|
16
|
+
/**
|
|
17
|
+
If a call is made while a previous call is still running, queue the latest arguments and run the function again after the current execution completes.
|
|
18
|
+
|
|
19
|
+
@default false
|
|
20
|
+
|
|
21
|
+
Use cases:
|
|
22
|
+
- With `after: false` (default): API fetches, data loading, read operations - concurrent calls share the same result.
|
|
23
|
+
- With `after: true`: Saving data, file writes, state updates - ensures latest data is never lost.
|
|
24
|
+
|
|
25
|
+
@example
|
|
26
|
+
```
|
|
27
|
+
import {setTimeout as delay} from 'timers/promises';
|
|
28
|
+
import pDebounce from '@cjser/p-debounce';
|
|
29
|
+
|
|
30
|
+
const save = async data => {
|
|
31
|
+
await delay(200);
|
|
32
|
+
console.log(`Saved: ${data}`);
|
|
33
|
+
return data;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const debouncedSave = pDebounce.promise(save, {after: true});
|
|
37
|
+
|
|
38
|
+
// If data changes while saving, it will save again with the latest data
|
|
39
|
+
debouncedSave('data1');
|
|
40
|
+
debouncedSave('data2'); // This will run after the first save completes
|
|
41
|
+
//=> Saved: data1
|
|
42
|
+
//=> Saved: data2
|
|
43
|
+
```
|
|
44
|
+
*/
|
|
45
|
+
readonly after?: boolean;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
declare const pDebounce: {
|
|
49
|
+
/**
|
|
50
|
+
[Debounce](https://css-tricks.com/debouncing-throttling-explained-examples/) promise-returning & async functions.
|
|
51
|
+
|
|
52
|
+
@param fn - Promise-returning/async function to debounce.
|
|
53
|
+
@param wait - Milliseconds to wait before calling `fn`.
|
|
54
|
+
@returns A function that delays calling `fn` until after `wait` milliseconds have elapsed since the last time it was called.
|
|
55
|
+
|
|
56
|
+
@example
|
|
57
|
+
```
|
|
58
|
+
import pDebounce from '@cjser/p-debounce';
|
|
59
|
+
|
|
60
|
+
const expensiveCall = async input => input;
|
|
61
|
+
|
|
62
|
+
const debouncedFunction = pDebounce(expensiveCall, 200);
|
|
63
|
+
|
|
64
|
+
for (const number of [1, 2, 3]) {
|
|
65
|
+
(async () => {
|
|
66
|
+
console.log(await debouncedFunction(number));
|
|
67
|
+
})();
|
|
68
|
+
}
|
|
69
|
+
//=> 3
|
|
70
|
+
//=> 3
|
|
71
|
+
//=> 3
|
|
72
|
+
```
|
|
73
|
+
*/
|
|
74
|
+
<This, ArgumentsType extends unknown[], ReturnType>(
|
|
75
|
+
fn: (this: This, ...arguments: ArgumentsType) => PromiseLike<ReturnType> | ReturnType,
|
|
76
|
+
wait: number,
|
|
77
|
+
options?: Options
|
|
78
|
+
): (this: This, ...arguments: ArgumentsType) => Promise<ReturnType>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
Execute `function_` unless a previous call is still pending, in which case, return the pending promise. Useful, for example, to avoid processing extra button clicks if the previous one is not complete.
|
|
82
|
+
|
|
83
|
+
@param function_ - Promise-returning/async function to debounce.
|
|
84
|
+
|
|
85
|
+
@example
|
|
86
|
+
```
|
|
87
|
+
import {setTimeout as delay} from 'timers/promises';
|
|
88
|
+
import pDebounce from '@cjser/p-debounce';
|
|
89
|
+
|
|
90
|
+
const expensiveCall = async value => {
|
|
91
|
+
await delay(200);
|
|
92
|
+
return value;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const debouncedFunction = pDebounce.promise(expensiveCall);
|
|
96
|
+
|
|
97
|
+
for (const number of [1, 2, 3]) {
|
|
98
|
+
(async () => {
|
|
99
|
+
console.log(await debouncedFunction(number));
|
|
100
|
+
})();
|
|
101
|
+
}
|
|
102
|
+
//=> 1
|
|
103
|
+
//=> 1
|
|
104
|
+
//=> 1
|
|
105
|
+
```
|
|
106
|
+
*/
|
|
107
|
+
promise<This, ArgumentsType extends unknown[], ReturnType>(
|
|
108
|
+
function_: (this: This, ...arguments: ArgumentsType) => PromiseLike<ReturnType> | ReturnType,
|
|
109
|
+
options?: PromiseOptions
|
|
110
|
+
): (this: This, ...arguments: ArgumentsType) => Promise<ReturnType>;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default pDebounce;
|
package/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const pDebounce = (functionToDebounce, wait, options = {}) => {
|
|
2
|
+
if (!Number.isFinite(wait)) {
|
|
3
|
+
throw new TypeError('Expected `wait` to be a finite number');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
let leadingValue;
|
|
7
|
+
let timeout;
|
|
8
|
+
let promiseHandlers = []; // Single array of {resolve, reject}
|
|
9
|
+
|
|
10
|
+
const onAbort = () => {
|
|
11
|
+
clearTimeout(timeout);
|
|
12
|
+
timeout = undefined;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
options.signal?.throwIfAborted();
|
|
16
|
+
} catch (error) {
|
|
17
|
+
for (const {reject} of promiseHandlers) {
|
|
18
|
+
reject(error);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
promiseHandlers = [];
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return function (...arguments_) {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
// Check if already aborted
|
|
28
|
+
try {
|
|
29
|
+
options.signal?.throwIfAborted();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
reject(error);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const shouldCallNow = options.before && !timeout;
|
|
36
|
+
|
|
37
|
+
clearTimeout(timeout);
|
|
38
|
+
|
|
39
|
+
timeout = setTimeout(async () => {
|
|
40
|
+
timeout = undefined;
|
|
41
|
+
|
|
42
|
+
// Capture the current handlers for this execution
|
|
43
|
+
const currentHandlers = promiseHandlers;
|
|
44
|
+
|
|
45
|
+
// Clear handlers for next cycle (new calls during execution will add to new list)
|
|
46
|
+
promiseHandlers = [];
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const result = options.before ? leadingValue : await functionToDebounce.apply(this, arguments_);
|
|
50
|
+
|
|
51
|
+
for (const {resolve: resolveFunction} of currentHandlers) {
|
|
52
|
+
resolveFunction(result);
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
for (const {reject: rejectFunction} of currentHandlers) {
|
|
56
|
+
rejectFunction(error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Clear leading value for next cycle
|
|
61
|
+
leadingValue = undefined;
|
|
62
|
+
|
|
63
|
+
// Remove abort listener
|
|
64
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
65
|
+
}, wait);
|
|
66
|
+
|
|
67
|
+
if (shouldCallNow) {
|
|
68
|
+
// Execute immediately for leading edge
|
|
69
|
+
(async () => {
|
|
70
|
+
try {
|
|
71
|
+
leadingValue = await functionToDebounce.apply(this, arguments_);
|
|
72
|
+
resolve(leadingValue);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
reject(error);
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
77
|
+
} else {
|
|
78
|
+
// Add to handlers for later resolution
|
|
79
|
+
promiseHandlers.push({resolve, reject});
|
|
80
|
+
|
|
81
|
+
// Set up abort listener (only once per batch)
|
|
82
|
+
if (options.signal && promiseHandlers.length === 1) {
|
|
83
|
+
options.signal.addEventListener('abort', onAbort, {once: true});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
pDebounce.promise = (function_, options = {}) => {
|
|
91
|
+
let currentPromise;
|
|
92
|
+
let queuedCall;
|
|
93
|
+
|
|
94
|
+
return async function (...arguments_) {
|
|
95
|
+
if (currentPromise) {
|
|
96
|
+
if (!options.after) {
|
|
97
|
+
return currentPromise;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Queue latest call (replacing any existing queue)
|
|
101
|
+
queuedCall ??= {resolvers: []};
|
|
102
|
+
queuedCall.arguments = arguments_;
|
|
103
|
+
queuedCall.context = this;
|
|
104
|
+
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
queuedCall.resolvers.push({resolve, reject});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
currentPromise = (async () => {
|
|
111
|
+
let result;
|
|
112
|
+
let initialError;
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
result = await function_.apply(this, arguments_);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
initialError = error;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Process queued calls regardless of initial result
|
|
121
|
+
while (queuedCall) {
|
|
122
|
+
const call = queuedCall;
|
|
123
|
+
queuedCall = undefined;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
// eslint-disable-next-line no-await-in-loop
|
|
127
|
+
const queuedResult = await function_.apply(call.context, call.arguments);
|
|
128
|
+
for (const {resolve} of call.resolvers) {
|
|
129
|
+
resolve(queuedResult);
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
for (const {reject} of call.resolvers) {
|
|
133
|
+
reject(error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (initialError) {
|
|
139
|
+
throw initialError;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return result;
|
|
143
|
+
})();
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
return await currentPromise;
|
|
147
|
+
} finally {
|
|
148
|
+
currentPromise = undefined;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export default pDebounce;
|
package/license
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cjser/p-debounce",
|
|
3
|
+
"version": "5.1.0-cjser.2",
|
|
4
|
+
"description": "Debounce promise-returning & async functions",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://code.moenext.com/3rdeye/cjser.git"
|
|
9
|
+
},
|
|
10
|
+
"funding": "https://github.com/sponsors/sindresorhus",
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "Sindre Sorhus",
|
|
13
|
+
"email": "sindresorhus@gmail.com",
|
|
14
|
+
"url": "https://sindresorhus.com"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
"types": "./index.d.ts",
|
|
19
|
+
"require": "./dist-cjser/index.cjs",
|
|
20
|
+
"default": "./index.js"
|
|
21
|
+
},
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test": "xo && node --test && tsd"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"index.js",
|
|
31
|
+
"index.d.ts",
|
|
32
|
+
"dist-cjser"
|
|
33
|
+
],
|
|
34
|
+
"keywords": [
|
|
35
|
+
"promise",
|
|
36
|
+
"debounce",
|
|
37
|
+
"debounced",
|
|
38
|
+
"limit",
|
|
39
|
+
"limited",
|
|
40
|
+
"concurrency",
|
|
41
|
+
"throttle",
|
|
42
|
+
"throat",
|
|
43
|
+
"interval",
|
|
44
|
+
"rate",
|
|
45
|
+
"batch",
|
|
46
|
+
"ratelimit",
|
|
47
|
+
"task",
|
|
48
|
+
"queue",
|
|
49
|
+
"async",
|
|
50
|
+
"await",
|
|
51
|
+
"promises",
|
|
52
|
+
"bluebird"
|
|
53
|
+
],
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"tsd": "^0.33.0",
|
|
56
|
+
"xo": "^1.2.2"
|
|
57
|
+
},
|
|
58
|
+
"types": "./index.d.ts",
|
|
59
|
+
"main": "./dist-cjser/index.cjs",
|
|
60
|
+
"cjser": {
|
|
61
|
+
"sourceVersion": "5.1.0",
|
|
62
|
+
"cjserVersion": 2,
|
|
63
|
+
"original": {
|
|
64
|
+
"name": "p-debounce",
|
|
65
|
+
"version": "5.1.0",
|
|
66
|
+
"exports": {
|
|
67
|
+
"types": "./index.d.ts",
|
|
68
|
+
"default": "./index.js"
|
|
69
|
+
},
|
|
70
|
+
"repository": "sindresorhus/p-debounce",
|
|
71
|
+
"files": [
|
|
72
|
+
"index.js",
|
|
73
|
+
"index.d.ts"
|
|
74
|
+
],
|
|
75
|
+
"scripts": {
|
|
76
|
+
"test": "xo && node --test && tsd"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# p-debounce
|
|
2
|
+
|
|
3
|
+
> [Debounce](https://css-tricks.com/debouncing-throttling-explained-examples/) promise-returning & async functions
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install p-debounce
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import pDebounce from 'p-debounce';
|
|
15
|
+
|
|
16
|
+
const expensiveCall = async input => input;
|
|
17
|
+
|
|
18
|
+
const debouncedFunction = pDebounce(expensiveCall, 200);
|
|
19
|
+
|
|
20
|
+
for (const number of [1, 2, 3]) {
|
|
21
|
+
(async () => {
|
|
22
|
+
console.log(await debouncedFunction(number));
|
|
23
|
+
})();
|
|
24
|
+
}
|
|
25
|
+
//=> 3
|
|
26
|
+
//=> 3
|
|
27
|
+
//=> 3
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## API
|
|
31
|
+
|
|
32
|
+
### pDebounce(fn, wait, options?)
|
|
33
|
+
|
|
34
|
+
Returns a function that delays calling `fn` until after `wait` milliseconds have elapsed since the last time it was called.
|
|
35
|
+
|
|
36
|
+
#### fn
|
|
37
|
+
|
|
38
|
+
Type: `Function`
|
|
39
|
+
|
|
40
|
+
Promise-returning/async function to debounce.
|
|
41
|
+
|
|
42
|
+
#### wait
|
|
43
|
+
|
|
44
|
+
Type: `number`
|
|
45
|
+
|
|
46
|
+
Milliseconds to wait before calling `fn`.
|
|
47
|
+
|
|
48
|
+
#### options
|
|
49
|
+
|
|
50
|
+
Type: `object`
|
|
51
|
+
|
|
52
|
+
##### before
|
|
53
|
+
|
|
54
|
+
Type: `boolean`\
|
|
55
|
+
Default: `false`
|
|
56
|
+
|
|
57
|
+
Call the `fn` on the [leading edge of the timeout](https://css-tricks.com/debouncing-throttling-explained-examples/#article-header-id-1). Meaning immediately, instead of waiting for `wait` milliseconds.
|
|
58
|
+
|
|
59
|
+
##### signal
|
|
60
|
+
|
|
61
|
+
Type: `AbortSignal`
|
|
62
|
+
|
|
63
|
+
An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the debounced function.
|
|
64
|
+
|
|
65
|
+
### pDebounce.promise(function_, options?)
|
|
66
|
+
|
|
67
|
+
Execute `function_` unless a previous call is still pending, in which case, return the pending promise. Useful, for example, to avoid processing extra button clicks if the previous one is not complete.
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
import {setTimeout as delay} from 'timers/promises';
|
|
71
|
+
import pDebounce from 'p-debounce';
|
|
72
|
+
|
|
73
|
+
const expensiveCall = async value => {
|
|
74
|
+
await delay(200);
|
|
75
|
+
return value;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const debouncedFunction = pDebounce.promise(expensiveCall);
|
|
79
|
+
|
|
80
|
+
for (const number of [1, 2, 3]) {
|
|
81
|
+
(async () => {
|
|
82
|
+
console.log(await debouncedFunction(number));
|
|
83
|
+
})();
|
|
84
|
+
}
|
|
85
|
+
//=> 1
|
|
86
|
+
//=> 1
|
|
87
|
+
//=> 1
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### function_
|
|
91
|
+
|
|
92
|
+
Type: `Function`
|
|
93
|
+
|
|
94
|
+
Promise-returning/async function to debounce.
|
|
95
|
+
|
|
96
|
+
#### options
|
|
97
|
+
|
|
98
|
+
Type: `object`
|
|
99
|
+
|
|
100
|
+
##### after
|
|
101
|
+
|
|
102
|
+
Type: `boolean`\
|
|
103
|
+
Default: `false`
|
|
104
|
+
|
|
105
|
+
If a call is made while a previous call is still running, queue the latest arguments and run the function again after the current execution completes.
|
|
106
|
+
|
|
107
|
+
Use cases:
|
|
108
|
+
- With `after: false` (default): API fetches, data loading, read operations - concurrent calls share the same result.
|
|
109
|
+
- With `after: true`: Saving data, file writes, state updates - ensures latest data is never lost.
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
import {setTimeout as delay} from 'timers/promises';
|
|
113
|
+
import pDebounce from 'p-debounce';
|
|
114
|
+
|
|
115
|
+
const save = async data => {
|
|
116
|
+
await delay(200);
|
|
117
|
+
console.log(`Saved: ${data}`);
|
|
118
|
+
return data;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const debouncedSave = pDebounce.promise(save, {after: true});
|
|
122
|
+
|
|
123
|
+
// If data changes while saving, it will save again with the latest data
|
|
124
|
+
debouncedSave('data1');
|
|
125
|
+
debouncedSave('data2'); // This will run after the first save completes
|
|
126
|
+
//=> Saved: data1
|
|
127
|
+
//=> Saved: data2
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Related
|
|
131
|
+
|
|
132
|
+
- [p-throttle](https://github.com/sindresorhus/p-throttle) - Throttle promise-returning & async functions
|
|
133
|
+
- [p-limit](https://github.com/sindresorhus/p-limit) - Run multiple promise-returning & async functions with limited concurrency
|
|
134
|
+
- [p-memoize](https://github.com/sindresorhus/p-memoize) - Memoize promise-returning & async functions
|
|
135
|
+
- [debounce-fn](https://github.com/sindresorhus/debounce-fn) - Debounce a function
|
|
136
|
+
- [More…](https://github.com/sindresorhus/promise-fun)
|
|
137
|
+
|
|
138
|
+
## cjser
|
|
139
|
+
|
|
140
|
+
This package is a CommonJS-compatible build generated by cjser for projects that still need `require()` support. The source version matches the original npm package version, with a cjser prerelease suffix for this generated build.
|
|
141
|
+
Original repository: https://github.com/sindresorhus/p-debounce
|