@x-oasis/throttle 0.1.37 → 0.1.38
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/.turbo/turbo-build.log +5 -11
- package/CHANGELOG.md +9 -0
- package/README.md +164 -7
- package/dist/index.d.ts +8 -3
- package/dist/throttle.cjs.development.js +14 -20
- package/dist/throttle.cjs.development.js.map +1 -1
- package/dist/throttle.cjs.production.min.js +1 -1
- package/dist/throttle.cjs.production.min.js.map +1 -1
- package/dist/throttle.esm.js +12 -20
- package/dist/throttle.esm.js.map +1 -1
- package/package.json +5 -1
- package/src/index.ts +65 -33
- package/test/test.spec.ts +233 -3
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
|
|
2
|
-
> @x-oasis/throttle@0.1.
|
|
2
|
+
> @x-oasis/throttle@0.1.38 build /home/runner/work/x-oasis/x-oasis/packages/schedule/throttle
|
|
3
3
|
> tsdx build --tsconfig tsconfig.build.json
|
|
4
4
|
|
|
5
5
|
@rollup/plugin-replace: 'preventAssignment' currently defaults to false. It is recommended to set this option to `true`, as the next major version will default this option to `true`.
|
|
6
6
|
@rollup/plugin-replace: 'preventAssignment' currently defaults to false. It is recommended to set this option to `true`, as the next major version will default this option to `true`.
|
|
7
7
|
⠙ Creating entry file
|
|
8
|
-
[2K[1A[2K[G
|
|
9
|
-
|
|
10
|
-
⠹ Building modules
|
|
8
|
+
[2K[1A[2K[G✓ Creating entry file 2.4 secs
|
|
9
|
+
⠙ Building modules
|
|
10
|
+
[2K[1A[2K[G⠹ Building modules
|
|
11
11
|
[2K[1A[2K[G⠸ Building modules
|
|
12
12
|
[2K[1A[2K[G⠼ Building modules
|
|
13
|
-
[2K[1A[2K[G⠴ Building modules
|
|
14
|
-
[2K[1A[2K[G⠦ Building modules
|
|
15
|
-
[2K[1A[2K[G⠧ Building modules
|
|
16
|
-
[2K[1A[2K[G⠇ Building modules
|
|
17
|
-
[2K[1A[2K[G⠏ Building modules
|
|
18
|
-
[2K[1A[2K[G⠋ Building modules
|
|
19
13
|
[tsdx]: Your rootDir is currently set to "./". Please change your rootDir to "./src".
|
|
20
14
|
TSDX has deprecated setting tsconfig.compilerOptions.rootDir to "./" as it caused buggy output for declarationMaps and more.
|
|
21
15
|
You may also need to change your include to remove "test", which also caused declarations to be unnecessarily created for test files.
|
|
22
|
-
[2K[1A[2K[G✓ Building modules
|
|
16
|
+
[2K[1A[2K[G✓ Building modules 3.2 secs
|
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,19 +1,176 @@
|
|
|
1
1
|
# @x-oasis/throttle
|
|
2
2
|
|
|
3
|
+
Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds.
|
|
4
|
+
|
|
3
5
|
## Installation
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
|
-
|
|
8
|
+
npm install @x-oasis/throttle
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @x-oasis/throttle
|
|
11
|
+
# or
|
|
12
|
+
yarn add @x-oasis/throttle
|
|
7
13
|
```
|
|
8
14
|
|
|
9
|
-
##
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Basic Usage
|
|
10
18
|
|
|
11
19
|
```typescript
|
|
12
|
-
import throttle from '@x-oasis/throttle'
|
|
20
|
+
import throttle from '@x-oasis/throttle';
|
|
21
|
+
|
|
22
|
+
// Avoid excessively updating the position while scrolling.
|
|
23
|
+
const updatePosition = () => {
|
|
24
|
+
// Update scroll position
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const throttled = throttle(updatePosition, 100);
|
|
28
|
+
window.addEventListener('scroll', throttled);
|
|
13
29
|
```
|
|
14
30
|
|
|
15
|
-
|
|
31
|
+
### With Options
|
|
16
32
|
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
```typescript
|
|
34
|
+
import throttle from '@x-oasis/throttle';
|
|
35
|
+
|
|
36
|
+
const renewToken = () => {
|
|
37
|
+
// Renew authentication token
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
|
|
41
|
+
const throttled = throttle(renewToken, 300000, {
|
|
42
|
+
trailing: false
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Cancel and Flush
|
|
47
|
+
|
|
48
|
+
The throttled function comes with a `cancel` method to cancel delayed `func` invocations and a `flush` method to immediately invoke them.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import throttle from '@x-oasis/throttle';
|
|
52
|
+
|
|
53
|
+
const throttled = throttle(updatePosition, 100);
|
|
54
|
+
|
|
55
|
+
// Cancel the trailing throttled invocation.
|
|
56
|
+
throttled.cancel();
|
|
57
|
+
|
|
58
|
+
// Flush the trailing throttled invocation.
|
|
59
|
+
throttled.flush();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
### `throttle(func, wait, options?)`
|
|
65
|
+
|
|
66
|
+
Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds.
|
|
67
|
+
|
|
68
|
+
#### Parameters
|
|
69
|
+
|
|
70
|
+
- `func` (`Function`): The function to throttle.
|
|
71
|
+
- `wait` (`number`, default: `0`): The number of milliseconds to throttle invocations to.
|
|
72
|
+
- `options` (`Object`, optional): The options object.
|
|
73
|
+
- `leading` (`boolean`, default: `true`): Specify invoking on the leading edge of the timeout.
|
|
74
|
+
- `trailing` (`boolean`, default: `true`): Specify invoking on the trailing edge of the timeout.
|
|
75
|
+
|
|
76
|
+
#### Returns
|
|
77
|
+
|
|
78
|
+
Returns the new throttled function with the following methods:
|
|
79
|
+
|
|
80
|
+
- `cancel()`: Cancels delayed `func` invocations.
|
|
81
|
+
- `flush()`: Immediately invokes the delayed `func` invocation.
|
|
82
|
+
|
|
83
|
+
## Examples
|
|
84
|
+
|
|
85
|
+
### Scroll Handler
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import throttle from '@x-oasis/throttle';
|
|
89
|
+
|
|
90
|
+
const handleScroll = () => {
|
|
91
|
+
// Update UI based on scroll position
|
|
92
|
+
console.log('Scroll position:', window.scrollY);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const throttledScroll = throttle(handleScroll, 100);
|
|
96
|
+
|
|
97
|
+
window.addEventListener('scroll', throttledScroll);
|
|
98
|
+
|
|
99
|
+
// Cleanup
|
|
100
|
+
window.removeEventListener('scroll', throttledScroll);
|
|
101
|
+
throttledScroll.cancel();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Mouse Move Handler
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import throttle from '@x-oasis/throttle';
|
|
108
|
+
|
|
109
|
+
const handleMouseMove = (event: MouseEvent) => {
|
|
110
|
+
// Track mouse position
|
|
111
|
+
console.log('Mouse position:', event.clientX, event.clientY);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const throttledMouseMove = throttle(handleMouseMove, 50);
|
|
115
|
+
|
|
116
|
+
document.addEventListener('mousemove', throttledMouseMove);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Leading and Trailing
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import throttle from '@x-oasis/throttle';
|
|
123
|
+
|
|
124
|
+
const logMessage = (message: string) => {
|
|
125
|
+
console.log(message);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Execute immediately on first call, then wait for trailing edge
|
|
129
|
+
const throttled = throttle(logMessage, 1000, {
|
|
130
|
+
leading: true,
|
|
131
|
+
trailing: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
throttled('First'); // Executes immediately
|
|
135
|
+
throttled('Second'); // Ignored
|
|
136
|
+
throttled('Third'); // Ignored
|
|
137
|
+
// After 1000ms, 'Third' executes (trailing edge)
|
|
138
|
+
|
|
139
|
+
// Only execute on trailing edge
|
|
140
|
+
const trailingOnly = throttle(logMessage, 1000, {
|
|
141
|
+
leading: false,
|
|
142
|
+
trailing: true
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
trailingOnly('First'); // Not executed immediately
|
|
146
|
+
trailingOnly('Second'); // Not executed
|
|
147
|
+
trailingOnly('Third'); // Not executed
|
|
148
|
+
// After 1000ms, 'Third' executes
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Differences from Debounce
|
|
152
|
+
|
|
153
|
+
- **Throttle**: Executes the function at most once per `wait` milliseconds, regardless of how many times it's called.
|
|
154
|
+
- **Debounce**: Delays execution until after `wait` milliseconds have elapsed since the last invocation.
|
|
155
|
+
|
|
156
|
+
### When to Use Throttle
|
|
157
|
+
|
|
158
|
+
- Scroll events
|
|
159
|
+
- Mouse move events
|
|
160
|
+
- Window resize events (when you want periodic updates)
|
|
161
|
+
- API calls that should happen at regular intervals
|
|
162
|
+
|
|
163
|
+
### When to Use Debounce
|
|
164
|
+
|
|
165
|
+
- Search input (wait for user to stop typing)
|
|
166
|
+
- Button clicks (prevent double-clicks)
|
|
167
|
+
- Window resize events (when you only care about the final size)
|
|
168
|
+
|
|
169
|
+
## See Also
|
|
170
|
+
|
|
171
|
+
- [Lodash throttle](https://lodash.com/docs/#throttle)
|
|
172
|
+
- [@x-oasis/debounce](../debounce/README.md) - Creates a debounced function
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
ISC
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export declare type ThrottleOptions = {
|
|
2
|
+
leading?: boolean;
|
|
3
|
+
trailing?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export default function throttle(func: (...args: any[]) => any, wait?: number, options?: ThrottleOptions): ((...args: any[]) => any) & {
|
|
6
|
+
cancel: () => void;
|
|
7
|
+
flush: () => void;
|
|
8
|
+
};
|
|
@@ -2,27 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
6
|
+
|
|
7
|
+
var debounce = _interopDefault(require('@x-oasis/debounce'));
|
|
8
|
+
|
|
9
|
+
function throttle(func, wait, options) {
|
|
10
|
+
if (wait === void 0) {
|
|
11
|
+
wait = 0;
|
|
8
12
|
}
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
_args = args;
|
|
18
|
-
if (typeof persistArgs === 'function') {
|
|
19
|
-
_args = persistArgs(args);
|
|
20
|
-
}
|
|
21
|
-
if (now - last > threshold) {
|
|
22
|
-
last = now;
|
|
23
|
-
return fn.apply(this, args);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
13
|
+
var leading = (options == null ? void 0 : options.leading) !== undefined ? options.leading : true;
|
|
14
|
+
var trailing = (options == null ? void 0 : options.trailing) !== undefined ? options.trailing : true;
|
|
15
|
+
return debounce(func, wait, {
|
|
16
|
+
leading: leading,
|
|
17
|
+
trailing: trailing,
|
|
18
|
+
maxWait: wait
|
|
19
|
+
});
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
exports.default = throttle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttle.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["/**\n *\n * @param
|
|
1
|
+
{"version":3,"file":"throttle.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\n\n/**\n * Options for throttle function\n * @see https://lodash.com/docs/#throttle\n */\nexport type ThrottleOptions = {\n /**\n * Specify invoking on the leading edge of the timeout.\n * @default true\n */\n leading?: boolean;\n /**\n * Specify invoking on the trailing edge of the timeout.\n * @default true\n */\n trailing?: boolean;\n};\n\n/**\n * Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds.\n *\n * The throttled function comes with a `cancel` method to cancel delayed `func` invocations\n * and a `flush` method to immediately invoke them.\n *\n * @param func - The function to throttle\n * @param wait - The number of milliseconds to throttle invocations to\n * @param options - The options object\n * @returns Returns the new throttled function\n *\n * @example\n * ```typescript\n * // Avoid excessively updating the position while scrolling.\n * const throttled = throttle(updatePosition, 100);\n * window.addEventListener('scroll', throttled);\n *\n * // Cancel the trailing throttled invocation.\n * throttled.cancel();\n *\n * // Flush the trailing throttled invocation.\n * throttled.flush();\n * ```\n *\n * @example\n * ```typescript\n * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n * const throttled = throttle(renewToken, 300000, {\n * 'trailing': false\n * });\n * ```\n *\n * @see https://lodash.com/docs/#throttle\n */\nexport default function throttle(\n func: (...args: any[]) => any,\n wait = 0,\n options?: ThrottleOptions\n): ((...args: any[]) => any) & {\n cancel: () => void;\n flush: () => void;\n} {\n const leading = options?.leading !== undefined ? options.leading : true;\n const trailing = options?.trailing !== undefined ? options.trailing : true;\n\n // Throttle is essentially debounce with maxWait set to wait\n // This ensures the function is invoked at most once per wait period\n return debounce(func, wait, {\n leading,\n trailing,\n maxWait: wait,\n });\n}\n"],"names":["throttle","func","wait","options","leading","undefined","trailing","debounce","maxWait"],"mappings":";;;;;;;;SAqDwBA,QAAQA,CAC9BC,IAA6B,EAC7BC,IAAI,EACJC,OAAyB;MADzBD,IAAI;IAAJA,IAAI,GAAG,CAAC;;EAMR,IAAME,OAAO,GAAG,CAAAD,OAAO,oBAAPA,OAAO,CAAEC,OAAO,MAAKC,SAAS,GAAGF,OAAO,CAACC,OAAO,GAAG,IAAI;EACvE,IAAME,QAAQ,GAAG,CAAAH,OAAO,oBAAPA,OAAO,CAAEG,QAAQ,MAAKD,SAAS,GAAGF,OAAO,CAACG,QAAQ,GAAG,IAAI;EAI1E,OAAOC,QAAQ,CAACN,IAAI,EAAEC,IAAI,EAAE;IAC1BE,OAAO,EAAPA,OAAO;IACPE,QAAQ,EAARA,QAAQ;IACRE,OAAO,EAAEN;GACV,CAAC;AACJ;;;;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=function(e,t,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,i=(e=require("@x-oasis/debounce"))&&"object"==typeof e&&"default"in e?e.default:e;exports.default=function(e,t,l){return void 0===t&&(t=0),i(e,t,{leading:void 0===(null==l?void 0:l.leading)||l.leading,trailing:void 0===(null==l?void 0:l.trailing)||l.trailing,maxWait:t})};
|
|
2
2
|
//# sourceMappingURL=throttle.cjs.production.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttle.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["/**\n *\n * @param
|
|
1
|
+
{"version":3,"file":"throttle.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\n\n/**\n * Options for throttle function\n * @see https://lodash.com/docs/#throttle\n */\nexport type ThrottleOptions = {\n /**\n * Specify invoking on the leading edge of the timeout.\n * @default true\n */\n leading?: boolean;\n /**\n * Specify invoking on the trailing edge of the timeout.\n * @default true\n */\n trailing?: boolean;\n};\n\n/**\n * Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds.\n *\n * The throttled function comes with a `cancel` method to cancel delayed `func` invocations\n * and a `flush` method to immediately invoke them.\n *\n * @param func - The function to throttle\n * @param wait - The number of milliseconds to throttle invocations to\n * @param options - The options object\n * @returns Returns the new throttled function\n *\n * @example\n * ```typescript\n * // Avoid excessively updating the position while scrolling.\n * const throttled = throttle(updatePosition, 100);\n * window.addEventListener('scroll', throttled);\n *\n * // Cancel the trailing throttled invocation.\n * throttled.cancel();\n *\n * // Flush the trailing throttled invocation.\n * throttled.flush();\n * ```\n *\n * @example\n * ```typescript\n * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n * const throttled = throttle(renewToken, 300000, {\n * 'trailing': false\n * });\n * ```\n *\n * @see https://lodash.com/docs/#throttle\n */\nexport default function throttle(\n func: (...args: any[]) => any,\n wait = 0,\n options?: ThrottleOptions\n): ((...args: any[]) => any) & {\n cancel: () => void;\n flush: () => void;\n} {\n const leading = options?.leading !== undefined ? options.leading : true;\n const trailing = options?.trailing !== undefined ? options.trailing : true;\n\n // Throttle is essentially debounce with maxWait set to wait\n // This ensures the function is invoked at most once per wait period\n return debounce(func, wait, {\n leading,\n trailing,\n maxWait: wait,\n });\n}\n"],"names":["func","wait","options","debounce","leading","undefined","trailing","maxWait"],"mappings":"qLAsDEA,EACAC,EACAC,GAUA,gBAXAD,IAAAA,EAAO,GAWAE,EAASH,EAAMC,EAAM,CAC1BG,aANmCC,WAArBH,SAAAA,EAASE,UAAwBF,EAAQE,QAOvDE,cANqCD,WAAtBH,SAAAA,EAASI,WAAyBJ,EAAQI,SAOzDC,QAASN"}
|
package/dist/throttle.esm.js
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import debounce from '@x-oasis/debounce';
|
|
2
|
+
|
|
3
|
+
function throttle(func, wait, options) {
|
|
4
|
+
if (wait === void 0) {
|
|
5
|
+
wait = 0;
|
|
4
6
|
}
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
_args = args;
|
|
14
|
-
if (typeof persistArgs === 'function') {
|
|
15
|
-
_args = persistArgs(args);
|
|
16
|
-
}
|
|
17
|
-
if (now - last > threshold) {
|
|
18
|
-
last = now;
|
|
19
|
-
return fn.apply(this, args);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
7
|
+
var leading = (options == null ? void 0 : options.leading) !== undefined ? options.leading : true;
|
|
8
|
+
var trailing = (options == null ? void 0 : options.trailing) !== undefined ? options.trailing : true;
|
|
9
|
+
return debounce(func, wait, {
|
|
10
|
+
leading: leading,
|
|
11
|
+
trailing: trailing,
|
|
12
|
+
maxWait: wait
|
|
13
|
+
});
|
|
22
14
|
}
|
|
23
15
|
|
|
24
16
|
export default throttle;
|
package/dist/throttle.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttle.esm.js","sources":["../src/index.ts"],"sourcesContent":["/**\n *\n * @param
|
|
1
|
+
{"version":3,"file":"throttle.esm.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\n\n/**\n * Options for throttle function\n * @see https://lodash.com/docs/#throttle\n */\nexport type ThrottleOptions = {\n /**\n * Specify invoking on the leading edge of the timeout.\n * @default true\n */\n leading?: boolean;\n /**\n * Specify invoking on the trailing edge of the timeout.\n * @default true\n */\n trailing?: boolean;\n};\n\n/**\n * Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds.\n *\n * The throttled function comes with a `cancel` method to cancel delayed `func` invocations\n * and a `flush` method to immediately invoke them.\n *\n * @param func - The function to throttle\n * @param wait - The number of milliseconds to throttle invocations to\n * @param options - The options object\n * @returns Returns the new throttled function\n *\n * @example\n * ```typescript\n * // Avoid excessively updating the position while scrolling.\n * const throttled = throttle(updatePosition, 100);\n * window.addEventListener('scroll', throttled);\n *\n * // Cancel the trailing throttled invocation.\n * throttled.cancel();\n *\n * // Flush the trailing throttled invocation.\n * throttled.flush();\n * ```\n *\n * @example\n * ```typescript\n * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n * const throttled = throttle(renewToken, 300000, {\n * 'trailing': false\n * });\n * ```\n *\n * @see https://lodash.com/docs/#throttle\n */\nexport default function throttle(\n func: (...args: any[]) => any,\n wait = 0,\n options?: ThrottleOptions\n): ((...args: any[]) => any) & {\n cancel: () => void;\n flush: () => void;\n} {\n const leading = options?.leading !== undefined ? options.leading : true;\n const trailing = options?.trailing !== undefined ? options.trailing : true;\n\n // Throttle is essentially debounce with maxWait set to wait\n // This ensures the function is invoked at most once per wait period\n return debounce(func, wait, {\n leading,\n trailing,\n maxWait: wait,\n });\n}\n"],"names":["throttle","func","wait","options","leading","undefined","trailing","debounce","maxWait"],"mappings":";;SAqDwBA,QAAQA,CAC9BC,IAA6B,EAC7BC,IAAI,EACJC,OAAyB;MADzBD,IAAI;IAAJA,IAAI,GAAG,CAAC;;EAMR,IAAME,OAAO,GAAG,CAAAD,OAAO,oBAAPA,OAAO,CAAEC,OAAO,MAAKC,SAAS,GAAGF,OAAO,CAACC,OAAO,GAAG,IAAI;EACvE,IAAME,QAAQ,GAAG,CAAAH,OAAO,oBAAPA,OAAO,CAAEG,QAAQ,MAAKD,SAAS,GAAGF,OAAO,CAACG,QAAQ,GAAG,IAAI;EAI1E,OAAOC,QAAQ,CAACN,IAAI,EAAEC,IAAI,EAAE;IAC1BE,OAAO,EAAPA,OAAO;IACPE,QAAQ,EAARA,QAAQ;IACRE,OAAO,EAAEN;GACV,CAAC;AACJ;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x-oasis/throttle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "throttle function",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"tsdx": "^0.14.1"
|
|
15
15
|
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@x-oasis/default-boolean-value": "0.1.38",
|
|
18
|
+
"@x-oasis/debounce": "0.1.38"
|
|
19
|
+
},
|
|
16
20
|
"scripts": {
|
|
17
21
|
"build": "tsdx build --tsconfig tsconfig.build.json",
|
|
18
22
|
"clean": "rimraf ./dist",
|
package/src/index.ts
CHANGED
|
@@ -1,40 +1,72 @@
|
|
|
1
|
+
import debounce from '@x-oasis/debounce';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for throttle function
|
|
5
|
+
* @see https://lodash.com/docs/#throttle
|
|
6
|
+
*/
|
|
7
|
+
export type ThrottleOptions = {
|
|
8
|
+
/**
|
|
9
|
+
* Specify invoking on the leading edge of the timeout.
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
leading?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Specify invoking on the trailing edge of the timeout.
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
trailing?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
1
20
|
/**
|
|
21
|
+
* Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds.
|
|
22
|
+
*
|
|
23
|
+
* The throttled function comes with a `cancel` method to cancel delayed `func` invocations
|
|
24
|
+
* and a `flush` method to immediately invoke them.
|
|
25
|
+
*
|
|
26
|
+
* @param func - The function to throttle
|
|
27
|
+
* @param wait - The number of milliseconds to throttle invocations to
|
|
28
|
+
* @param options - The options object
|
|
29
|
+
* @returns Returns the new throttled function
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // Avoid excessively updating the position while scrolling.
|
|
34
|
+
* const throttled = throttle(updatePosition, 100);
|
|
35
|
+
* window.addEventListener('scroll', throttled);
|
|
36
|
+
*
|
|
37
|
+
* // Cancel the trailing throttled invocation.
|
|
38
|
+
* throttled.cancel();
|
|
2
39
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
40
|
+
* // Flush the trailing throttled invocation.
|
|
41
|
+
* throttled.flush();
|
|
42
|
+
* ```
|
|
6
43
|
*
|
|
7
|
-
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
|
|
47
|
+
* const throttled = throttle(renewToken, 300000, {
|
|
48
|
+
* 'trailing': false
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @see https://lodash.com/docs/#throttle
|
|
8
53
|
*/
|
|
9
|
-
|
|
10
54
|
export default function throttle(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let _args = [];
|
|
21
|
-
|
|
22
|
-
return function throttled(...args: any[]) {
|
|
23
|
-
const now = Date.now();
|
|
24
|
-
|
|
25
|
-
const persistArgs =
|
|
26
|
-
typeof fallbackOptions === 'object' ? fallbackOptions.persistArgs : null;
|
|
27
|
-
|
|
28
|
-
_args = args;
|
|
29
|
-
|
|
30
|
-
if (typeof persistArgs === 'function') {
|
|
31
|
-
_args = persistArgs(args);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (now - last > threshold) {
|
|
35
|
-
last = now;
|
|
55
|
+
func: (...args: any[]) => any,
|
|
56
|
+
wait = 0,
|
|
57
|
+
options?: ThrottleOptions
|
|
58
|
+
): ((...args: any[]) => any) & {
|
|
59
|
+
cancel: () => void;
|
|
60
|
+
flush: () => void;
|
|
61
|
+
} {
|
|
62
|
+
const leading = options?.leading !== undefined ? options.leading : true;
|
|
63
|
+
const trailing = options?.trailing !== undefined ? options.trailing : true;
|
|
36
64
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
65
|
+
// Throttle is essentially debounce with maxWait set to wait
|
|
66
|
+
// This ensures the function is invoked at most once per wait period
|
|
67
|
+
return debounce(func, wait, {
|
|
68
|
+
leading,
|
|
69
|
+
trailing,
|
|
70
|
+
maxWait: wait,
|
|
71
|
+
});
|
|
40
72
|
}
|
package/test/test.spec.ts
CHANGED
|
@@ -1,5 +1,235 @@
|
|
|
1
|
-
import { expect, test } from 'vitest';
|
|
1
|
+
import { expect, test, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import throttle from '../src/index';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.useFakeTimers();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.restoreAllMocks();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('throttle basic functionality', () => {
|
|
13
|
+
const fn = vi.fn();
|
|
14
|
+
const throttled = throttle(fn, 100);
|
|
15
|
+
|
|
16
|
+
throttled(1);
|
|
17
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
18
|
+
expect(fn).toHaveBeenCalledWith(1);
|
|
19
|
+
|
|
20
|
+
throttled(2);
|
|
21
|
+
throttled(3);
|
|
22
|
+
expect(fn).toHaveBeenCalledTimes(1); // Still only called once
|
|
23
|
+
|
|
24
|
+
vi.advanceTimersByTime(100);
|
|
25
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
26
|
+
expect(fn).toHaveBeenCalledWith(3); // Last call's arguments
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('throttle with leading: false', () => {
|
|
30
|
+
const fn = vi.fn();
|
|
31
|
+
const throttled = throttle(fn, 100, { leading: false });
|
|
32
|
+
|
|
33
|
+
throttled(1);
|
|
34
|
+
expect(fn).not.toHaveBeenCalled();
|
|
35
|
+
|
|
36
|
+
vi.advanceTimersByTime(100);
|
|
37
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
38
|
+
expect(fn).toHaveBeenCalledWith(1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('throttle with trailing: false', () => {
|
|
42
|
+
const fn = vi.fn();
|
|
43
|
+
const throttled = throttle(fn, 100, { trailing: false });
|
|
44
|
+
|
|
45
|
+
throttled(1);
|
|
46
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
47
|
+
expect(fn).toHaveBeenCalledWith(1);
|
|
48
|
+
|
|
49
|
+
throttled(2);
|
|
50
|
+
throttled(3);
|
|
51
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
52
|
+
|
|
53
|
+
vi.advanceTimersByTime(100);
|
|
54
|
+
expect(fn).toHaveBeenCalledTimes(1); // No trailing execution
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('throttle with leading: false and trailing: false', () => {
|
|
58
|
+
const fn = vi.fn();
|
|
59
|
+
const throttled = throttle(fn, 100, {
|
|
60
|
+
leading: false,
|
|
61
|
+
trailing: false,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
throttled(1);
|
|
65
|
+
throttled(2);
|
|
66
|
+
throttled(3);
|
|
67
|
+
|
|
68
|
+
expect(fn).not.toHaveBeenCalled();
|
|
69
|
+
|
|
70
|
+
vi.advanceTimersByTime(100);
|
|
71
|
+
expect(fn).not.toHaveBeenCalled();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('throttle cancel', () => {
|
|
75
|
+
const fn = vi.fn();
|
|
76
|
+
const throttled = throttle(fn, 100);
|
|
77
|
+
|
|
78
|
+
throttled(1);
|
|
79
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
80
|
+
|
|
81
|
+
throttled(2);
|
|
82
|
+
throttled.cancel();
|
|
83
|
+
|
|
84
|
+
vi.advanceTimersByTime(100);
|
|
85
|
+
expect(fn).toHaveBeenCalledTimes(1); // Trailing call cancelled
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('throttle flush', () => {
|
|
89
|
+
const fn = vi.fn();
|
|
90
|
+
const throttled = throttle(fn, 100);
|
|
91
|
+
|
|
92
|
+
throttled(1);
|
|
93
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
94
|
+
|
|
95
|
+
throttled(2);
|
|
96
|
+
throttled(3);
|
|
97
|
+
throttled.flush();
|
|
98
|
+
|
|
99
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
100
|
+
expect(fn).toHaveBeenCalledWith(3);
|
|
101
|
+
|
|
102
|
+
vi.advanceTimersByTime(100);
|
|
103
|
+
expect(fn).toHaveBeenCalledTimes(2); // Already flushed
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('throttle preserves this context', () => {
|
|
107
|
+
const obj = {
|
|
108
|
+
value: 42,
|
|
109
|
+
fn(this: any, arg: number) {
|
|
110
|
+
return this.value + arg;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const throttled = throttle(obj.fn, 100);
|
|
115
|
+
throttled.call(obj, 10);
|
|
116
|
+
|
|
117
|
+
expect(obj.value).toBe(42);
|
|
118
|
+
// Function executes with correct context
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('throttle multiple calls within wait period', () => {
|
|
122
|
+
const fn = vi.fn();
|
|
123
|
+
const throttled = throttle(fn, 100);
|
|
124
|
+
|
|
125
|
+
throttled(1);
|
|
126
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
127
|
+
expect(fn).toHaveBeenCalledWith(1);
|
|
128
|
+
|
|
129
|
+
vi.advanceTimersByTime(50);
|
|
130
|
+
throttled(2);
|
|
131
|
+
expect(fn).toHaveBeenCalledTimes(1); // Still only called once
|
|
132
|
+
|
|
133
|
+
vi.advanceTimersByTime(50);
|
|
134
|
+
// After 100ms total, should execute immediately with latest args
|
|
135
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
136
|
+
expect(fn).toHaveBeenCalledWith(2);
|
|
137
|
+
|
|
138
|
+
vi.advanceTimersByTime(50);
|
|
139
|
+
throttled(3);
|
|
140
|
+
expect(fn).toHaveBeenCalledTimes(3);
|
|
141
|
+
expect(fn).toHaveBeenCalledWith(3);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('throttle with leading and trailing both true', () => {
|
|
145
|
+
const fn = vi.fn();
|
|
146
|
+
const throttled = throttle(fn, 100, {
|
|
147
|
+
leading: true,
|
|
148
|
+
trailing: true,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
throttled(1);
|
|
152
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
153
|
+
expect(fn).toHaveBeenCalledWith(1);
|
|
154
|
+
|
|
155
|
+
throttled(2);
|
|
156
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
157
|
+
|
|
158
|
+
vi.advanceTimersByTime(100);
|
|
159
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
160
|
+
expect(fn).toHaveBeenCalledWith(2);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('throttle rapid successive calls', () => {
|
|
164
|
+
const fn = vi.fn();
|
|
165
|
+
const throttled = throttle(fn, 100);
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < 10; i++) {
|
|
168
|
+
throttled(i);
|
|
169
|
+
vi.advanceTimersByTime(10);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Should be called multiple times (once per 100ms)
|
|
173
|
+
expect(fn).toHaveBeenCalled();
|
|
174
|
+
expect(fn.mock.calls.length).toBeGreaterThan(1);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('throttle with zero wait time', () => {
|
|
178
|
+
const fn = vi.fn();
|
|
179
|
+
const throttled = throttle(fn, 0);
|
|
180
|
+
|
|
181
|
+
throttled(1);
|
|
182
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
183
|
+
|
|
184
|
+
throttled(2);
|
|
185
|
+
vi.advanceTimersByTime(0);
|
|
186
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('throttle cancel after flush', () => {
|
|
190
|
+
const fn = vi.fn();
|
|
191
|
+
const throttled = throttle(fn, 100);
|
|
192
|
+
|
|
193
|
+
throttled(1);
|
|
194
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
195
|
+
|
|
196
|
+
throttled(2);
|
|
197
|
+
throttled.flush();
|
|
198
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
199
|
+
|
|
200
|
+
throttled.cancel();
|
|
201
|
+
vi.advanceTimersByTime(100);
|
|
202
|
+
expect(fn).toHaveBeenCalledTimes(2); // No additional call
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('throttle maintains last arguments', () => {
|
|
206
|
+
const fn = vi.fn();
|
|
207
|
+
const throttled = throttle(fn, 100);
|
|
208
|
+
|
|
209
|
+
throttled('first');
|
|
210
|
+
expect(fn).toHaveBeenCalledWith('first');
|
|
211
|
+
|
|
212
|
+
throttled('second');
|
|
213
|
+
throttled('third');
|
|
214
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
215
|
+
|
|
216
|
+
vi.advanceTimersByTime(100);
|
|
217
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
218
|
+
expect(fn).toHaveBeenCalledWith('third'); // Last call's arguments
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('throttle with multiple arguments', () => {
|
|
222
|
+
const fn = vi.fn();
|
|
223
|
+
const throttled = throttle(fn, 100);
|
|
224
|
+
|
|
225
|
+
throttled(1, 'a', true);
|
|
226
|
+
expect(fn).toHaveBeenCalledWith(1, 'a', true);
|
|
227
|
+
|
|
228
|
+
throttled(2, 'b', false);
|
|
229
|
+
throttled(3, 'c', true);
|
|
230
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
231
|
+
|
|
232
|
+
vi.advanceTimersByTime(100);
|
|
233
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
234
|
+
expect(fn).toHaveBeenCalledWith(3, 'c', true);
|
|
5
235
|
});
|