@x-oasis/batchinator 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 -3
- package/CHANGELOG.md +9 -0
- package/README.md +269 -7
- package/dist/batchinator.cjs.development.js +40 -36
- package/dist/batchinator.cjs.development.js.map +1 -1
- package/dist/batchinator.cjs.production.min.js +1 -1
- package/dist/batchinator.cjs.production.min.js.map +1 -1
- package/dist/batchinator.esm.js +40 -36
- package/dist/batchinator.esm.js.map +1 -1
- package/dist/index.d.ts +10 -7
- package/package.json +3 -2
- package/src/index.ts +87 -54
- package/test/test.spec.ts +261 -4
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
> @x-oasis/batchinator@0.1.
|
|
2
|
+
> @x-oasis/batchinator@0.1.38 build /home/runner/work/x-oasis/x-oasis/packages/schedule/batchinator
|
|
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✓ Creating entry file 2 secs
|
|
8
|
+
[2K[1A[2K[G✓ Creating entry file 2.5 secs
|
|
9
9
|
⠙ Building modules
|
|
10
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
|
|
13
15
|
[tsdx]: Your rootDir is currently set to "./". Please change your rootDir to "./src".
|
|
14
16
|
TSDX has deprecated setting tsconfig.compilerOptions.rootDir to "./" as it caused buggy output for declarationMaps and more.
|
|
15
17
|
You may also need to change your include to remove "test", which also caused declarations to be unnecessarily created for test files.
|
|
16
|
-
[2K[1A[2K[G✓ Building modules
|
|
18
|
+
[2K[1A[2K[G✓ Building modules 4.1 secs
|
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,19 +1,281 @@
|
|
|
1
1
|
# @x-oasis/batchinator
|
|
2
2
|
|
|
3
|
+
Batches callback executions with configurable leading/trailing behavior. Similar to debounce/throttle but with more control over execution timing.
|
|
4
|
+
|
|
5
|
+
Inspired by [React Native's Batchinator](https://github.com/facebook/react-native/blob/main/Libraries/Interaction/Batchinator.js).
|
|
6
|
+
|
|
3
7
|
## Installation
|
|
4
8
|
|
|
5
9
|
```bash
|
|
6
|
-
|
|
10
|
+
npm install @x-oasis/batchinator
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @x-oasis/batchinator
|
|
13
|
+
# or
|
|
14
|
+
yarn add @x-oasis/batchinator
|
|
7
15
|
```
|
|
8
16
|
|
|
9
|
-
##
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Basic Usage
|
|
10
20
|
|
|
11
21
|
```typescript
|
|
12
|
-
import Batchinator from '@x-oasis/batchinator'
|
|
22
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
23
|
+
|
|
24
|
+
const callback = (message: string) => {
|
|
25
|
+
console.log(message);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const batchinator = new Batchinator(callback, 100);
|
|
29
|
+
|
|
30
|
+
// Schedule multiple calls
|
|
31
|
+
batchinator.schedule('First');
|
|
32
|
+
batchinator.schedule('Second');
|
|
33
|
+
batchinator.schedule('Third');
|
|
34
|
+
|
|
35
|
+
// Only the last call ('Third') will execute after 100ms
|
|
13
36
|
```
|
|
14
37
|
|
|
15
|
-
|
|
38
|
+
### With Leading Option
|
|
16
39
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
40
|
+
Execute immediately on the first call, then batch subsequent calls:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
44
|
+
|
|
45
|
+
const batchinator = new Batchinator(callback, 100, {
|
|
46
|
+
leading: true,
|
|
47
|
+
trailing: true
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
batchinator.schedule('First'); // Executes immediately
|
|
51
|
+
batchinator.schedule('Second'); // Batched
|
|
52
|
+
batchinator.schedule('Third'); // Batched
|
|
53
|
+
// After 100ms, 'Third' executes (trailing)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### With Trailing: false
|
|
57
|
+
|
|
58
|
+
Only execute on the leading edge:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
62
|
+
|
|
63
|
+
const batchinator = new Batchinator(callback, 100, {
|
|
64
|
+
leading: true,
|
|
65
|
+
trailing: false
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
batchinator.schedule('First'); // Executes immediately
|
|
69
|
+
batchinator.schedule('Second'); // Ignored
|
|
70
|
+
batchinator.schedule('Third'); // Ignored
|
|
71
|
+
// No trailing execution
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Flush Scheduled Task
|
|
75
|
+
|
|
76
|
+
Immediately execute the scheduled task:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
80
|
+
|
|
81
|
+
const batchinator = new Batchinator(callback, 100);
|
|
82
|
+
|
|
83
|
+
batchinator.schedule('First');
|
|
84
|
+
batchinator.schedule('Second');
|
|
85
|
+
batchinator.flush(); // Executes immediately with 'Second'
|
|
86
|
+
|
|
87
|
+
// Or flush with new arguments
|
|
88
|
+
batchinator.flush('Custom');
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Dispose
|
|
92
|
+
|
|
93
|
+
Cancel the scheduled task and optionally execute it:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
97
|
+
|
|
98
|
+
const batchinator = new Batchinator(callback, 100);
|
|
99
|
+
|
|
100
|
+
batchinator.schedule('First');
|
|
101
|
+
batchinator.schedule('Second');
|
|
102
|
+
|
|
103
|
+
// Dispose and execute
|
|
104
|
+
batchinator.dispose(); // Executes with 'Second'
|
|
105
|
+
|
|
106
|
+
// Dispose without executing
|
|
107
|
+
batchinator.dispose({ abort: true }); // Cancels without executing
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Check Schedule Status
|
|
111
|
+
|
|
112
|
+
Check if a task is currently scheduled:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
116
|
+
|
|
117
|
+
const batchinator = new Batchinator(callback, 100);
|
|
118
|
+
|
|
119
|
+
batchinator.schedule('First');
|
|
120
|
+
console.log(batchinator.inSchedule()); // true
|
|
121
|
+
|
|
122
|
+
// After execution or cancel
|
|
123
|
+
batchinator.flush();
|
|
124
|
+
console.log(batchinator.inSchedule()); // false
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## API
|
|
128
|
+
|
|
129
|
+
### `new Batchinator(callback, delayMS, options?)`
|
|
130
|
+
|
|
131
|
+
Creates a new Batchinator instance.
|
|
132
|
+
|
|
133
|
+
#### Parameters
|
|
134
|
+
|
|
135
|
+
- `callback` (`Function`): The function to batch.
|
|
136
|
+
- `delayMS` (`number`): The delay in milliseconds before executing the batched callback.
|
|
137
|
+
- `options` (`Object`, optional): Configuration options.
|
|
138
|
+
- `leading` (`boolean`, default: `false`): Execute immediately on the first call.
|
|
139
|
+
- `trailing` (`boolean`, default: `true`): Execute after the delay period.
|
|
140
|
+
|
|
141
|
+
#### Returns
|
|
142
|
+
|
|
143
|
+
Returns a new `Batchinator` instance.
|
|
144
|
+
|
|
145
|
+
### Instance Methods
|
|
146
|
+
|
|
147
|
+
#### `schedule(...args)`
|
|
148
|
+
|
|
149
|
+
Schedule the callback execution with the given arguments. If called multiple times, only the last call's arguments will be used.
|
|
150
|
+
|
|
151
|
+
- `args` (`...any[]`): Arguments to pass to the callback.
|
|
152
|
+
|
|
153
|
+
#### `flush(...args)`
|
|
154
|
+
|
|
155
|
+
Immediately execute the scheduled task. If arguments are provided, they will be used instead of stored arguments.
|
|
156
|
+
|
|
157
|
+
- `args` (`...any[]`, optional): Optional arguments to use instead of stored args.
|
|
158
|
+
|
|
159
|
+
#### `dispose(options?)`
|
|
160
|
+
|
|
161
|
+
Dispose the scheduled task. By default, executes the callback with stored arguments unless `abort` is `true`.
|
|
162
|
+
|
|
163
|
+
- `options` (`Object`, optional): Configuration options.
|
|
164
|
+
- `abort` (`boolean`, default: `false`): If `true`, cancel without executing callback.
|
|
165
|
+
|
|
166
|
+
#### `inSchedule()`
|
|
167
|
+
|
|
168
|
+
Check if a task is currently scheduled.
|
|
169
|
+
|
|
170
|
+
- Returns: `boolean` - `true` if a task is scheduled, `false` otherwise.
|
|
171
|
+
|
|
172
|
+
## Examples
|
|
173
|
+
|
|
174
|
+
### React Native Interaction Manager
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
178
|
+
|
|
179
|
+
// Batch UI updates to avoid blocking the main thread
|
|
180
|
+
const updateUI = (data: any) => {
|
|
181
|
+
// Update UI with data
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const batchinator = new Batchinator(updateUI, 16, {
|
|
185
|
+
leading: false,
|
|
186
|
+
trailing: true
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Multiple rapid updates
|
|
190
|
+
for (let i = 0; i < 100; i++) {
|
|
191
|
+
batchinator.schedule({ index: i });
|
|
192
|
+
}
|
|
193
|
+
// Only the last update executes after 16ms
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Form Input Batching
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
200
|
+
|
|
201
|
+
const saveForm = (formData: any) => {
|
|
202
|
+
// Save form data
|
|
203
|
+
console.log('Saving:', formData);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const batchinator = new Batchinator(saveForm, 500, {
|
|
207
|
+
leading: false,
|
|
208
|
+
trailing: true
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// User types in form
|
|
212
|
+
input.addEventListener('input', (e) => {
|
|
213
|
+
batchinator.schedule({ value: e.target.value });
|
|
214
|
+
});
|
|
215
|
+
// Form saves 500ms after user stops typing
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Immediate Execution with Batching
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
222
|
+
|
|
223
|
+
const logMessage = (message: string) => {
|
|
224
|
+
console.log(message);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const batchinator = new Batchinator(logMessage, 1000, {
|
|
228
|
+
leading: true,
|
|
229
|
+
trailing: true
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
batchinator.schedule('First'); // Executes immediately
|
|
233
|
+
batchinator.schedule('Second'); // Batched
|
|
234
|
+
batchinator.schedule('Third'); // Batched
|
|
235
|
+
// After 1000ms, 'Third' executes
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Cleanup on Component Unmount
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import Batchinator from '@x-oasis/batchinator';
|
|
242
|
+
import { useEffect } from 'react';
|
|
243
|
+
|
|
244
|
+
function MyComponent() {
|
|
245
|
+
const batchinator = new Batchinator(handleUpdate, 100);
|
|
246
|
+
|
|
247
|
+
useEffect(() => {
|
|
248
|
+
// Use batchinator
|
|
249
|
+
batchinator.schedule(data);
|
|
250
|
+
|
|
251
|
+
// Cleanup on unmount
|
|
252
|
+
return () => {
|
|
253
|
+
batchinator.dispose({ abort: true });
|
|
254
|
+
};
|
|
255
|
+
}, []);
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Differences from Debounce/Throttle
|
|
260
|
+
|
|
261
|
+
- **Batchinator**: Batches multiple calls and executes with the last arguments. Supports leading/trailing execution.
|
|
262
|
+
- **Debounce**: Delays execution until after a period of inactivity.
|
|
263
|
+
- **Throttle**: Limits execution to at most once per period.
|
|
264
|
+
|
|
265
|
+
### When to Use Batchinator
|
|
266
|
+
|
|
267
|
+
- React Native Interaction Manager scenarios
|
|
268
|
+
- Batching UI updates
|
|
269
|
+
- Form auto-save (with leading: false, trailing: true)
|
|
270
|
+
- Event handling where you want immediate feedback but batched processing
|
|
271
|
+
|
|
272
|
+
## See Also
|
|
273
|
+
|
|
274
|
+
- [React Native Batchinator](https://github.com/facebook/react-native/blob/main/Libraries/Interaction/Batchinator.js)
|
|
275
|
+
- [@x-oasis/debounce](../debounce/README.md) - Creates a debounced function
|
|
276
|
+
- [@x-oasis/throttle](../throttle/README.md) - Creates a throttled function
|
|
277
|
+
- [@x-oasis/batchinate-last](../batchinate-last/README.md) - Executes with last arguments
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
ISC
|
|
@@ -4,15 +4,27 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
6
6
|
|
|
7
|
+
var debounce = _interopDefault(require('@x-oasis/debounce'));
|
|
7
8
|
var defaultBooleanValue = _interopDefault(require('@x-oasis/default-boolean-value'));
|
|
8
9
|
|
|
9
10
|
var Batchinator = /*#__PURE__*/function () {
|
|
10
11
|
function Batchinator(cb, delayMS, options) {
|
|
12
|
+
var _this = this;
|
|
13
|
+
this._isScheduled = false;
|
|
14
|
+
this._storedArgs = null;
|
|
11
15
|
this._callback = cb;
|
|
12
16
|
this._delayMS = delayMS;
|
|
13
|
-
this._taskHandle = null;
|
|
14
|
-
this._args = null;
|
|
15
17
|
this._leading = defaultBooleanValue(options == null ? void 0 : options.leading, false);
|
|
18
|
+
this._trailing = defaultBooleanValue(options == null ? void 0 : options.trailing, true);
|
|
19
|
+
this._debounced = debounce(function () {
|
|
20
|
+
_this._isScheduled = false;
|
|
21
|
+
if (_this._storedArgs !== null) {
|
|
22
|
+
_this._callback.apply(_this, _this._storedArgs);
|
|
23
|
+
}
|
|
24
|
+
}, delayMS, {
|
|
25
|
+
leading: this._leading,
|
|
26
|
+
trailing: this._trailing
|
|
27
|
+
});
|
|
16
28
|
}
|
|
17
29
|
var _proto = Batchinator.prototype;
|
|
18
30
|
_proto.dispose = function dispose(options) {
|
|
@@ -22,57 +34,49 @@ var Batchinator = /*#__PURE__*/function () {
|
|
|
22
34
|
};
|
|
23
35
|
}
|
|
24
36
|
var _options = options,
|
|
25
|
-
abort = _options.abort
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
_options$abort = _options.abort,
|
|
38
|
+
abort = _options$abort === void 0 ? false : _options$abort;
|
|
39
|
+
if (abort) {
|
|
40
|
+
this._debounced.cancel();
|
|
41
|
+
this._isScheduled = false;
|
|
42
|
+
this._storedArgs = null;
|
|
43
|
+
} else {
|
|
44
|
+
if (this._storedArgs !== null) {
|
|
45
|
+
this._debounced.flush();
|
|
46
|
+
}
|
|
32
47
|
}
|
|
33
48
|
};
|
|
34
49
|
_proto.inSchedule = function inSchedule() {
|
|
35
|
-
return
|
|
50
|
+
return this._isScheduled;
|
|
36
51
|
};
|
|
37
52
|
_proto.flush = function flush() {
|
|
38
53
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
39
54
|
args[_key] = arguments[_key];
|
|
40
55
|
}
|
|
41
|
-
if (args.length
|
|
42
|
-
|
|
43
|
-
this._taskHandle.cancel();
|
|
44
|
-
this._taskHandle = null;
|
|
56
|
+
if (args.length > 0) {
|
|
57
|
+
this._storedArgs = args;
|
|
45
58
|
}
|
|
46
|
-
this.
|
|
59
|
+
this._debounced.flush();
|
|
60
|
+
this._isScheduled = false;
|
|
47
61
|
};
|
|
48
62
|
_proto.schedule = function schedule() {
|
|
49
|
-
var _this = this;
|
|
50
63
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
51
64
|
args[_key2] = arguments[_key2];
|
|
52
65
|
}
|
|
53
|
-
this.
|
|
54
|
-
if (this.
|
|
55
|
-
var handler = this._leading ? function () {
|
|
56
|
-
_this._taskHandle = null;
|
|
57
|
-
} : function () {
|
|
58
|
-
_this._taskHandle = null;
|
|
59
|
-
_this._callback.apply(_this, _this._args);
|
|
60
|
-
};
|
|
61
|
-
if (!this._delayMS) {
|
|
62
|
-
handler();
|
|
66
|
+
this._storedArgs = args;
|
|
67
|
+
if (this._isScheduled) {
|
|
63
68
|
return;
|
|
64
69
|
}
|
|
65
|
-
if (this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}, this._delayMS);
|
|
71
|
-
this._taskHandle = {
|
|
72
|
-
cancel: function cancel() {
|
|
73
|
-
return clearTimeout(timeoutHandle);
|
|
70
|
+
if (!this._delayMS) {
|
|
71
|
+
if (this._leading) {
|
|
72
|
+
this._callback.apply(this, this._storedArgs);
|
|
73
|
+
} else if (this._trailing) {
|
|
74
|
+
this._callback.apply(this, this._storedArgs);
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this._isScheduled = true;
|
|
79
|
+
this._debounced();
|
|
76
80
|
};
|
|
77
81
|
return Batchinator;
|
|
78
82
|
}();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchinator.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"batchinator.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\nimport defaultBooleanValue from '@x-oasis/default-boolean-value';\n\n// https://github.com/facebook/react-native/blob/main/Libraries/Interaction/Batchinator.js\n\ntype BatchinatorOptions = {\n leading?: boolean;\n trailing?: boolean;\n};\n\n/**\n * Batchinator - Batches callback executions with configurable leading/trailing behavior.\n * Similar to debounce/throttle but with more control over execution timing.\n */\nclass Batchinator {\n readonly _delayMS: number;\n private _callback: (...args: any[]) => void;\n private _debounced: ReturnType<typeof debounce>;\n private _leading: boolean;\n private _trailing: boolean;\n private _isScheduled = false;\n private _storedArgs: any[] | null = null;\n\n constructor(\n cb: (...args: any[]) => void,\n delayMS: number,\n options?: BatchinatorOptions\n ) {\n this._callback = cb;\n this._delayMS = delayMS;\n this._leading = defaultBooleanValue(options?.leading, false);\n this._trailing = defaultBooleanValue(options?.trailing, true);\n\n // Create a debounced function that wraps our callback\n // The key difference from debounce: if already scheduled, schedule() only updates args\n // We handle this by tracking _isScheduled and only calling debounce on first schedule\n this._debounced = debounce(\n () => {\n this._isScheduled = false;\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n },\n delayMS,\n {\n leading: this._leading,\n trailing: this._trailing,\n }\n );\n }\n\n /**\n * Dispose the scheduled task\n * @param options - Configuration options\n * @param options.abort - If true, cancel without executing callback\n */\n dispose(\n options: {\n abort?: boolean;\n } = {\n abort: false,\n }\n ): void {\n const { abort = false } = options;\n if (abort) {\n this._debounced.cancel();\n this._isScheduled = false;\n this._storedArgs = null;\n } else {\n // Execute with current args if any\n if (this._storedArgs !== null) {\n this._debounced.flush();\n }\n }\n }\n\n /**\n * Check if a task is currently scheduled\n */\n inSchedule(): boolean {\n return this._isScheduled;\n }\n\n /**\n * Flush the scheduled task immediately\n * @param args - Optional arguments to use instead of stored args\n */\n flush(...args: any[]): void {\n if (args.length > 0) {\n this._storedArgs = args;\n }\n this._debounced.flush();\n this._isScheduled = false;\n }\n\n /**\n * Schedule the callback execution\n * @param args - Arguments to pass to the callback\n */\n schedule(...args: any[]): void {\n this._storedArgs = args;\n\n // If already scheduled, just update args and return (don't reset timer)\n // This is the key difference from debounce\n if (this._isScheduled) {\n return;\n }\n\n // Handle zero delay case - execute immediately based on leading/trailing\n if (!this._delayMS) {\n if (this._leading) {\n this._callback(...this._storedArgs);\n } else if (this._trailing) {\n // For zero delay with trailing, execute immediately\n this._callback(...this._storedArgs);\n }\n return;\n }\n\n // First call - mark as scheduled and call debounce\n this._isScheduled = true;\n this._debounced();\n }\n}\n\nexport default Batchinator;\n"],"names":["Batchinator","cb","delayMS","options","_callback","_delayMS","_leading","defaultBooleanValue","leading","_trailing","trailing","_debounced","debounce","_this","_isScheduled","_storedArgs","apply","_proto","prototype","dispose","abort","_options","_options$abort","cancel","flush","inSchedule","args","Array","_len","_key","arguments","length","schedule","_len2","_key2"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var
|
|
1
|
+
"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var i=e(require("@x-oasis/debounce")),t=e(require("@x-oasis/default-boolean-value"));exports.default=function(){function e(e,s,l){var d=this;this._isScheduled=!1,this._storedArgs=null,this._callback=e,this._delayMS=s,this._leading=t(null==l?void 0:l.leading,!1),this._trailing=t(null==l?void 0:l.trailing,!0),this._debounced=i((function(){d._isScheduled=!1,null!==d._storedArgs&&d._callback.apply(d,d._storedArgs)}),s,{leading:this._leading,trailing:this._trailing})}var s=e.prototype;return s.dispose=function(e){void 0===e&&(e={abort:!1});var i=e.abort;void 0!==i&&i?(this._debounced.cancel(),this._isScheduled=!1,this._storedArgs=null):null!==this._storedArgs&&this._debounced.flush()},s.inSchedule=function(){return this._isScheduled},s.flush=function(){for(var e=arguments.length,i=new Array(e),t=0;t<e;t++)i[t]=arguments[t];i.length>0&&(this._storedArgs=i),this._debounced.flush(),this._isScheduled=!1},s.schedule=function(){for(var e=arguments.length,i=new Array(e),t=0;t<e;t++)i[t]=arguments[t];this._storedArgs=i,this._isScheduled||(this._delayMS?(this._isScheduled=!0,this._debounced()):(this._leading||this._trailing)&&this._callback.apply(this,this._storedArgs))},e}();
|
|
2
2
|
//# sourceMappingURL=batchinator.cjs.production.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchinator.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"batchinator.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\nimport defaultBooleanValue from '@x-oasis/default-boolean-value';\n\n// https://github.com/facebook/react-native/blob/main/Libraries/Interaction/Batchinator.js\n\ntype BatchinatorOptions = {\n leading?: boolean;\n trailing?: boolean;\n};\n\n/**\n * Batchinator - Batches callback executions with configurable leading/trailing behavior.\n * Similar to debounce/throttle but with more control over execution timing.\n */\nclass Batchinator {\n readonly _delayMS: number;\n private _callback: (...args: any[]) => void;\n private _debounced: ReturnType<typeof debounce>;\n private _leading: boolean;\n private _trailing: boolean;\n private _isScheduled = false;\n private _storedArgs: any[] | null = null;\n\n constructor(\n cb: (...args: any[]) => void,\n delayMS: number,\n options?: BatchinatorOptions\n ) {\n this._callback = cb;\n this._delayMS = delayMS;\n this._leading = defaultBooleanValue(options?.leading, false);\n this._trailing = defaultBooleanValue(options?.trailing, true);\n\n // Create a debounced function that wraps our callback\n // The key difference from debounce: if already scheduled, schedule() only updates args\n // We handle this by tracking _isScheduled and only calling debounce on first schedule\n this._debounced = debounce(\n () => {\n this._isScheduled = false;\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n },\n delayMS,\n {\n leading: this._leading,\n trailing: this._trailing,\n }\n );\n }\n\n /**\n * Dispose the scheduled task\n * @param options - Configuration options\n * @param options.abort - If true, cancel without executing callback\n */\n dispose(\n options: {\n abort?: boolean;\n } = {\n abort: false,\n }\n ): void {\n const { abort = false } = options;\n if (abort) {\n this._debounced.cancel();\n this._isScheduled = false;\n this._storedArgs = null;\n } else {\n // Execute with current args if any\n if (this._storedArgs !== null) {\n this._debounced.flush();\n }\n }\n }\n\n /**\n * Check if a task is currently scheduled\n */\n inSchedule(): boolean {\n return this._isScheduled;\n }\n\n /**\n * Flush the scheduled task immediately\n * @param args - Optional arguments to use instead of stored args\n */\n flush(...args: any[]): void {\n if (args.length > 0) {\n this._storedArgs = args;\n }\n this._debounced.flush();\n this._isScheduled = false;\n }\n\n /**\n * Schedule the callback execution\n * @param args - Arguments to pass to the callback\n */\n schedule(...args: any[]): void {\n this._storedArgs = args;\n\n // If already scheduled, just update args and return (don't reset timer)\n // This is the key difference from debounce\n if (this._isScheduled) {\n return;\n }\n\n // Handle zero delay case - execute immediately based on leading/trailing\n if (!this._delayMS) {\n if (this._leading) {\n this._callback(...this._storedArgs);\n } else if (this._trailing) {\n // For zero delay with trailing, execute immediately\n this._callback(...this._storedArgs);\n }\n return;\n }\n\n // First call - mark as scheduled and call debounce\n this._isScheduled = true;\n this._debounced();\n }\n}\n\nexport default Batchinator;\n"],"names":["Batchinator","cb","delayMS","options","this","_callback","_delayMS","_leading","defaultBooleanValue","leading","_trailing","trailing","_debounced","debounce","_this","_isScheduled","_storedArgs","apply","_proto","prototype","dispose","abort","_options$abort","cancel","flush","inSchedule","args","Array","_len","_key","arguments","length","schedule","_len2","_key2"],"mappings":"0PAuBE,SAAAA,EACEC,EACAC,EACAC,cANMC,mBAAe,EACfA,iBAA4B,KAOlCA,KAAKC,UAAYJ,EACjBG,KAAKE,SAAWJ,EAChBE,KAAKG,SAAWC,QAAoBL,SAAAA,EAASM,SAAS,GACtDL,KAAKM,UAAYF,QAAoBL,SAAAA,EAASQ,UAAU,GAKxDP,KAAKQ,WAAaC,GAChB,WACEC,EAAKC,cAAe,EACK,OAArBD,EAAKE,aACPF,EAAKT,UAASY,MAAdH,EAAkBA,EAAKE,eAG3Bd,EACA,CACEO,QAASL,KAAKG,SACdI,SAAUP,KAAKM,YAGpB,IAAAQ,EAAAlB,EAAAmB,UAyEA,OAzEAD,EAODE,QAAA,SACEjB,YAAAA,IAAAA,EAEI,CACFkB,OAAO,IAGT,IAAiCC,EAAPnB,EAAlBkB,eAAKC,GAAQA,GAEnBlB,KAAKQ,WAAWW,SAChBnB,KAAKW,cAAe,EACpBX,KAAKY,YAAc,MAGM,OAArBZ,KAAKY,aACPZ,KAAKQ,WAAWY,SAGrBN,EAKDO,WAAA,WACE,OAAOrB,KAAKW,cACbG,EAMDM,MAAA,sCAASE,MAAWC,MAAAC,GAAAC,IAAAA,EAAAD,EAAAC,IAAXH,EAAWG,GAAAC,UAAAD,GACdH,EAAKK,OAAS,IAChB3B,KAAKY,YAAcU,GAErBtB,KAAKQ,WAAWY,QAChBpB,KAAKW,cAAe,GACrBG,EAMDc,SAAA,sCAAYN,MAAWC,MAAAM,GAAAC,IAAAA,EAAAD,EAAAC,IAAXR,EAAWQ,GAAAJ,UAAAI,GACrB9B,KAAKY,YAAcU,EAIftB,KAAKW,eAKJX,KAAKE,UAWVF,KAAKW,cAAe,EACpBX,KAAKQ,eAXCR,KAAKG,UAEEH,KAAKM,YADdN,KAAKC,UAASY,MAAdb,KAAkBA,KAAKY,eAW5BhB"}
|
package/dist/batchinator.esm.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
|
+
import debounce from '@x-oasis/debounce';
|
|
1
2
|
import defaultBooleanValue from '@x-oasis/default-boolean-value';
|
|
2
3
|
|
|
3
4
|
var Batchinator = /*#__PURE__*/function () {
|
|
4
5
|
function Batchinator(cb, delayMS, options) {
|
|
6
|
+
var _this = this;
|
|
7
|
+
this._isScheduled = false;
|
|
8
|
+
this._storedArgs = null;
|
|
5
9
|
this._callback = cb;
|
|
6
10
|
this._delayMS = delayMS;
|
|
7
|
-
this._taskHandle = null;
|
|
8
|
-
this._args = null;
|
|
9
11
|
this._leading = defaultBooleanValue(options == null ? void 0 : options.leading, false);
|
|
12
|
+
this._trailing = defaultBooleanValue(options == null ? void 0 : options.trailing, true);
|
|
13
|
+
this._debounced = debounce(function () {
|
|
14
|
+
_this._isScheduled = false;
|
|
15
|
+
if (_this._storedArgs !== null) {
|
|
16
|
+
_this._callback.apply(_this, _this._storedArgs);
|
|
17
|
+
}
|
|
18
|
+
}, delayMS, {
|
|
19
|
+
leading: this._leading,
|
|
20
|
+
trailing: this._trailing
|
|
21
|
+
});
|
|
10
22
|
}
|
|
11
23
|
var _proto = Batchinator.prototype;
|
|
12
24
|
_proto.dispose = function dispose(options) {
|
|
@@ -16,57 +28,49 @@ var Batchinator = /*#__PURE__*/function () {
|
|
|
16
28
|
};
|
|
17
29
|
}
|
|
18
30
|
var _options = options,
|
|
19
|
-
abort = _options.abort
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
_options$abort = _options.abort,
|
|
32
|
+
abort = _options$abort === void 0 ? false : _options$abort;
|
|
33
|
+
if (abort) {
|
|
34
|
+
this._debounced.cancel();
|
|
35
|
+
this._isScheduled = false;
|
|
36
|
+
this._storedArgs = null;
|
|
37
|
+
} else {
|
|
38
|
+
if (this._storedArgs !== null) {
|
|
39
|
+
this._debounced.flush();
|
|
40
|
+
}
|
|
26
41
|
}
|
|
27
42
|
};
|
|
28
43
|
_proto.inSchedule = function inSchedule() {
|
|
29
|
-
return
|
|
44
|
+
return this._isScheduled;
|
|
30
45
|
};
|
|
31
46
|
_proto.flush = function flush() {
|
|
32
47
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
33
48
|
args[_key] = arguments[_key];
|
|
34
49
|
}
|
|
35
|
-
if (args.length
|
|
36
|
-
|
|
37
|
-
this._taskHandle.cancel();
|
|
38
|
-
this._taskHandle = null;
|
|
50
|
+
if (args.length > 0) {
|
|
51
|
+
this._storedArgs = args;
|
|
39
52
|
}
|
|
40
|
-
this.
|
|
53
|
+
this._debounced.flush();
|
|
54
|
+
this._isScheduled = false;
|
|
41
55
|
};
|
|
42
56
|
_proto.schedule = function schedule() {
|
|
43
|
-
var _this = this;
|
|
44
57
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
45
58
|
args[_key2] = arguments[_key2];
|
|
46
59
|
}
|
|
47
|
-
this.
|
|
48
|
-
if (this.
|
|
49
|
-
var handler = this._leading ? function () {
|
|
50
|
-
_this._taskHandle = null;
|
|
51
|
-
} : function () {
|
|
52
|
-
_this._taskHandle = null;
|
|
53
|
-
_this._callback.apply(_this, _this._args);
|
|
54
|
-
};
|
|
55
|
-
if (!this._delayMS) {
|
|
56
|
-
handler();
|
|
60
|
+
this._storedArgs = args;
|
|
61
|
+
if (this._isScheduled) {
|
|
57
62
|
return;
|
|
58
63
|
}
|
|
59
|
-
if (this.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}, this._delayMS);
|
|
65
|
-
this._taskHandle = {
|
|
66
|
-
cancel: function cancel() {
|
|
67
|
-
return clearTimeout(timeoutHandle);
|
|
64
|
+
if (!this._delayMS) {
|
|
65
|
+
if (this._leading) {
|
|
66
|
+
this._callback.apply(this, this._storedArgs);
|
|
67
|
+
} else if (this._trailing) {
|
|
68
|
+
this._callback.apply(this, this._storedArgs);
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this._isScheduled = true;
|
|
73
|
+
this._debounced();
|
|
70
74
|
};
|
|
71
75
|
return Batchinator;
|
|
72
76
|
}();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchinator.esm.js","sources":["../src/index.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"batchinator.esm.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\nimport defaultBooleanValue from '@x-oasis/default-boolean-value';\n\n// https://github.com/facebook/react-native/blob/main/Libraries/Interaction/Batchinator.js\n\ntype BatchinatorOptions = {\n leading?: boolean;\n trailing?: boolean;\n};\n\n/**\n * Batchinator - Batches callback executions with configurable leading/trailing behavior.\n * Similar to debounce/throttle but with more control over execution timing.\n */\nclass Batchinator {\n readonly _delayMS: number;\n private _callback: (...args: any[]) => void;\n private _debounced: ReturnType<typeof debounce>;\n private _leading: boolean;\n private _trailing: boolean;\n private _isScheduled = false;\n private _storedArgs: any[] | null = null;\n\n constructor(\n cb: (...args: any[]) => void,\n delayMS: number,\n options?: BatchinatorOptions\n ) {\n this._callback = cb;\n this._delayMS = delayMS;\n this._leading = defaultBooleanValue(options?.leading, false);\n this._trailing = defaultBooleanValue(options?.trailing, true);\n\n // Create a debounced function that wraps our callback\n // The key difference from debounce: if already scheduled, schedule() only updates args\n // We handle this by tracking _isScheduled and only calling debounce on first schedule\n this._debounced = debounce(\n () => {\n this._isScheduled = false;\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n },\n delayMS,\n {\n leading: this._leading,\n trailing: this._trailing,\n }\n );\n }\n\n /**\n * Dispose the scheduled task\n * @param options - Configuration options\n * @param options.abort - If true, cancel without executing callback\n */\n dispose(\n options: {\n abort?: boolean;\n } = {\n abort: false,\n }\n ): void {\n const { abort = false } = options;\n if (abort) {\n this._debounced.cancel();\n this._isScheduled = false;\n this._storedArgs = null;\n } else {\n // Execute with current args if any\n if (this._storedArgs !== null) {\n this._debounced.flush();\n }\n }\n }\n\n /**\n * Check if a task is currently scheduled\n */\n inSchedule(): boolean {\n return this._isScheduled;\n }\n\n /**\n * Flush the scheduled task immediately\n * @param args - Optional arguments to use instead of stored args\n */\n flush(...args: any[]): void {\n if (args.length > 0) {\n this._storedArgs = args;\n }\n this._debounced.flush();\n this._isScheduled = false;\n }\n\n /**\n * Schedule the callback execution\n * @param args - Arguments to pass to the callback\n */\n schedule(...args: any[]): void {\n this._storedArgs = args;\n\n // If already scheduled, just update args and return (don't reset timer)\n // This is the key difference from debounce\n if (this._isScheduled) {\n return;\n }\n\n // Handle zero delay case - execute immediately based on leading/trailing\n if (!this._delayMS) {\n if (this._leading) {\n this._callback(...this._storedArgs);\n } else if (this._trailing) {\n // For zero delay with trailing, execute immediately\n this._callback(...this._storedArgs);\n }\n return;\n }\n\n // First call - mark as scheduled and call debounce\n this._isScheduled = true;\n this._debounced();\n }\n}\n\nexport default Batchinator;\n"],"names":["Batchinator","cb","delayMS","options","_callback","_delayMS","_leading","defaultBooleanValue","leading","_trailing","trailing","_debounced","debounce","_this","_isScheduled","_storedArgs","apply","_proto","prototype","dispose","abort","_options","_options$abort","cancel","flush","inSchedule","args","Array","_len","_key","arguments","length","schedule","_len2","_key2"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
declare type BatchinatorOptions = {
|
|
2
|
+
leading?: boolean;
|
|
3
|
+
trailing?: boolean;
|
|
4
|
+
};
|
|
1
5
|
declare class Batchinator {
|
|
2
6
|
readonly _delayMS: number;
|
|
3
|
-
private _args;
|
|
4
7
|
private _callback;
|
|
5
|
-
private
|
|
8
|
+
private _debounced;
|
|
6
9
|
private _leading;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
private _trailing;
|
|
11
|
+
private _isScheduled;
|
|
12
|
+
private _storedArgs;
|
|
13
|
+
constructor(cb: (...args: any[]) => void, delayMS: number, options?: BatchinatorOptions);
|
|
11
14
|
dispose(options?: {
|
|
12
|
-
abort
|
|
15
|
+
abort?: boolean;
|
|
13
16
|
}): void;
|
|
14
17
|
inSchedule(): boolean;
|
|
15
18
|
flush(...args: any[]): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x-oasis/batchinator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "batchinator function",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"tsdx": "^0.14.1"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@x-oasis/
|
|
17
|
+
"@x-oasis/debounce": "0.1.38",
|
|
18
|
+
"@x-oasis/default-boolean-value": "0.1.38"
|
|
18
19
|
},
|
|
19
20
|
"scripts": {
|
|
20
21
|
"build": "tsdx build --tsconfig tsconfig.build.json",
|
package/src/index.ts
CHANGED
|
@@ -1,92 +1,125 @@
|
|
|
1
|
+
import debounce from '@x-oasis/debounce';
|
|
1
2
|
import defaultBooleanValue from '@x-oasis/default-boolean-value';
|
|
2
|
-
// import { InteractionManager } from 'react-native';
|
|
3
3
|
|
|
4
4
|
// https://github.com/facebook/react-native/blob/main/Libraries/Interaction/Batchinator.js
|
|
5
5
|
|
|
6
|
+
type BatchinatorOptions = {
|
|
7
|
+
leading?: boolean;
|
|
8
|
+
trailing?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Batchinator - Batches callback executions with configurable leading/trailing behavior.
|
|
13
|
+
* Similar to debounce/throttle but with more control over execution timing.
|
|
14
|
+
*/
|
|
6
15
|
class Batchinator {
|
|
7
16
|
readonly _delayMS: number;
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
private _callback: Function;
|
|
11
|
-
private _taskHandle: {
|
|
12
|
-
cancel: () => void;
|
|
13
|
-
};
|
|
17
|
+
private _callback: (...args: any[]) => void;
|
|
18
|
+
private _debounced: ReturnType<typeof debounce>;
|
|
14
19
|
private _leading: boolean;
|
|
15
|
-
|
|
20
|
+
private _trailing: boolean;
|
|
21
|
+
private _isScheduled = false;
|
|
22
|
+
private _storedArgs: any[] | null = null;
|
|
16
23
|
|
|
17
24
|
constructor(
|
|
18
|
-
cb:
|
|
25
|
+
cb: (...args: any[]) => void,
|
|
19
26
|
delayMS: number,
|
|
20
|
-
options?:
|
|
21
|
-
leading: boolean;
|
|
22
|
-
trailing: boolean;
|
|
23
|
-
}
|
|
27
|
+
options?: BatchinatorOptions
|
|
24
28
|
) {
|
|
25
29
|
this._callback = cb;
|
|
26
30
|
this._delayMS = delayMS;
|
|
27
|
-
this._taskHandle = null;
|
|
28
|
-
this._args = null;
|
|
29
31
|
this._leading = defaultBooleanValue(options?.leading, false);
|
|
30
|
-
|
|
32
|
+
this._trailing = defaultBooleanValue(options?.trailing, true);
|
|
33
|
+
|
|
34
|
+
// Create a debounced function that wraps our callback
|
|
35
|
+
// The key difference from debounce: if already scheduled, schedule() only updates args
|
|
36
|
+
// We handle this by tracking _isScheduled and only calling debounce on first schedule
|
|
37
|
+
this._debounced = debounce(
|
|
38
|
+
() => {
|
|
39
|
+
this._isScheduled = false;
|
|
40
|
+
if (this._storedArgs !== null) {
|
|
41
|
+
this._callback(...this._storedArgs);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
delayMS,
|
|
45
|
+
{
|
|
46
|
+
leading: this._leading,
|
|
47
|
+
trailing: this._trailing,
|
|
48
|
+
}
|
|
49
|
+
);
|
|
31
50
|
}
|
|
32
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Dispose the scheduled task
|
|
54
|
+
* @param options - Configuration options
|
|
55
|
+
* @param options.abort - If true, cancel without executing callback
|
|
56
|
+
*/
|
|
33
57
|
dispose(
|
|
34
58
|
options: {
|
|
35
|
-
abort
|
|
59
|
+
abort?: boolean;
|
|
36
60
|
} = {
|
|
37
61
|
abort: false,
|
|
38
62
|
}
|
|
39
|
-
) {
|
|
40
|
-
const { abort } = options;
|
|
41
|
-
if (
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
): void {
|
|
64
|
+
const { abort = false } = options;
|
|
65
|
+
if (abort) {
|
|
66
|
+
this._debounced.cancel();
|
|
67
|
+
this._isScheduled = false;
|
|
68
|
+
this._storedArgs = null;
|
|
69
|
+
} else {
|
|
70
|
+
// Execute with current args if any
|
|
71
|
+
if (this._storedArgs !== null) {
|
|
72
|
+
this._debounced.flush();
|
|
73
|
+
}
|
|
47
74
|
}
|
|
48
75
|
}
|
|
49
76
|
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Check if a task is currently scheduled
|
|
79
|
+
*/
|
|
80
|
+
inSchedule(): boolean {
|
|
81
|
+
return this._isScheduled;
|
|
52
82
|
}
|
|
53
83
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Flush the scheduled task immediately
|
|
86
|
+
* @param args - Optional arguments to use instead of stored args
|
|
87
|
+
*/
|
|
88
|
+
flush(...args: any[]): void {
|
|
89
|
+
if (args.length > 0) {
|
|
90
|
+
this._storedArgs = args;
|
|
59
91
|
}
|
|
60
|
-
this.
|
|
92
|
+
this._debounced.flush();
|
|
93
|
+
this._isScheduled = false;
|
|
61
94
|
}
|
|
62
95
|
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Schedule the callback execution
|
|
98
|
+
* @param args - Arguments to pass to the callback
|
|
99
|
+
*/
|
|
100
|
+
schedule(...args: any[]): void {
|
|
101
|
+
this._storedArgs = args;
|
|
65
102
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
this._taskHandle = null;
|
|
70
|
-
}
|
|
71
|
-
: () => {
|
|
72
|
-
this._taskHandle = null;
|
|
73
|
-
this._callback.apply(this, this._args);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
if (!this._delayMS) {
|
|
77
|
-
handler();
|
|
103
|
+
// If already scheduled, just update args and return (don't reset timer)
|
|
104
|
+
// This is the key difference from debounce
|
|
105
|
+
if (this._isScheduled) {
|
|
78
106
|
return;
|
|
79
107
|
}
|
|
80
108
|
|
|
81
|
-
|
|
82
|
-
|
|
109
|
+
// Handle zero delay case - execute immediately based on leading/trailing
|
|
110
|
+
if (!this._delayMS) {
|
|
111
|
+
if (this._leading) {
|
|
112
|
+
this._callback(...this._storedArgs);
|
|
113
|
+
} else if (this._trailing) {
|
|
114
|
+
// For zero delay with trailing, execute immediately
|
|
115
|
+
this._callback(...this._storedArgs);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
83
118
|
}
|
|
84
119
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this._taskHandle = { cancel: () => clearTimeout(timeoutHandle) };
|
|
120
|
+
// First call - mark as scheduled and call debounce
|
|
121
|
+
this._isScheduled = true;
|
|
122
|
+
this._debounced();
|
|
90
123
|
}
|
|
91
124
|
}
|
|
92
125
|
|
package/test/test.spec.ts
CHANGED
|
@@ -1,5 +1,262 @@
|
|
|
1
|
-
import { expect, test } from 'vitest'
|
|
1
|
+
import { expect, test, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Batchinator from '../src/index';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
})
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.useFakeTimers();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.restoreAllMocks();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('Batchinator basic functionality - batches calls', () => {
|
|
13
|
+
const fn = vi.fn();
|
|
14
|
+
const batchinator = new Batchinator(fn, 100);
|
|
15
|
+
|
|
16
|
+
batchinator.schedule('First');
|
|
17
|
+
batchinator.schedule('Second');
|
|
18
|
+
batchinator.schedule('Third');
|
|
19
|
+
|
|
20
|
+
expect(fn).not.toHaveBeenCalled();
|
|
21
|
+
|
|
22
|
+
vi.advanceTimersByTime(100);
|
|
23
|
+
|
|
24
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
25
|
+
expect(fn).toHaveBeenCalledWith('Third');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('Batchinator with leading: true', () => {
|
|
29
|
+
const fn = vi.fn();
|
|
30
|
+
const batchinator = new Batchinator(fn, 100, { leading: true });
|
|
31
|
+
|
|
32
|
+
batchinator.schedule('First');
|
|
33
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(fn).toHaveBeenCalledWith('First');
|
|
35
|
+
|
|
36
|
+
batchinator.schedule('Second');
|
|
37
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
38
|
+
|
|
39
|
+
vi.advanceTimersByTime(100);
|
|
40
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
41
|
+
expect(fn).toHaveBeenCalledWith('Second');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('Batchinator with trailing: false', () => {
|
|
45
|
+
const fn = vi.fn();
|
|
46
|
+
const batchinator = new Batchinator(fn, 100, { trailing: false });
|
|
47
|
+
|
|
48
|
+
batchinator.schedule('First');
|
|
49
|
+
batchinator.schedule('Second');
|
|
50
|
+
batchinator.schedule('Third');
|
|
51
|
+
|
|
52
|
+
expect(fn).not.toHaveBeenCalled();
|
|
53
|
+
|
|
54
|
+
vi.advanceTimersByTime(100);
|
|
55
|
+
expect(fn).not.toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('Batchinator with leading: true and trailing: false', () => {
|
|
59
|
+
const fn = vi.fn();
|
|
60
|
+
const batchinator = new Batchinator(fn, 100, {
|
|
61
|
+
leading: true,
|
|
62
|
+
trailing: false,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
batchinator.schedule('First');
|
|
66
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
67
|
+
expect(fn).toHaveBeenCalledWith('First');
|
|
68
|
+
|
|
69
|
+
batchinator.schedule('Second');
|
|
70
|
+
batchinator.schedule('Third');
|
|
71
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
72
|
+
|
|
73
|
+
vi.advanceTimersByTime(100);
|
|
74
|
+
expect(fn).toHaveBeenCalledTimes(1); // No trailing execution
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('Batchinator with leading: false and trailing: true (default)', () => {
|
|
78
|
+
const fn = vi.fn();
|
|
79
|
+
const batchinator = new Batchinator(fn, 100, {
|
|
80
|
+
leading: false,
|
|
81
|
+
trailing: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
batchinator.schedule('First');
|
|
85
|
+
expect(fn).not.toHaveBeenCalled();
|
|
86
|
+
|
|
87
|
+
vi.advanceTimersByTime(100);
|
|
88
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
89
|
+
expect(fn).toHaveBeenCalledWith('First');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('Batchinator flush', () => {
|
|
93
|
+
const fn = vi.fn();
|
|
94
|
+
const batchinator = new Batchinator(fn, 100);
|
|
95
|
+
|
|
96
|
+
batchinator.schedule('First');
|
|
97
|
+
batchinator.schedule('Second');
|
|
98
|
+
batchinator.flush();
|
|
99
|
+
|
|
100
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
101
|
+
expect(fn).toHaveBeenCalledWith('Second');
|
|
102
|
+
|
|
103
|
+
vi.advanceTimersByTime(100);
|
|
104
|
+
expect(fn).toHaveBeenCalledTimes(1); // Already flushed
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('Batchinator flush with new arguments', () => {
|
|
108
|
+
const fn = vi.fn();
|
|
109
|
+
const batchinator = new Batchinator(fn, 100);
|
|
110
|
+
|
|
111
|
+
batchinator.schedule('First');
|
|
112
|
+
batchinator.flush('Custom');
|
|
113
|
+
|
|
114
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
115
|
+
expect(fn).toHaveBeenCalledWith('Custom');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('Batchinator dispose without abort', () => {
|
|
119
|
+
const fn = vi.fn();
|
|
120
|
+
const batchinator = new Batchinator(fn, 100);
|
|
121
|
+
|
|
122
|
+
batchinator.schedule('First');
|
|
123
|
+
batchinator.schedule('Second');
|
|
124
|
+
batchinator.dispose();
|
|
125
|
+
|
|
126
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
127
|
+
expect(fn).toHaveBeenCalledWith('Second');
|
|
128
|
+
|
|
129
|
+
vi.advanceTimersByTime(100);
|
|
130
|
+
expect(fn).toHaveBeenCalledTimes(1); // Already disposed
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('Batchinator dispose with abort', () => {
|
|
134
|
+
const fn = vi.fn();
|
|
135
|
+
const batchinator = new Batchinator(fn, 100);
|
|
136
|
+
|
|
137
|
+
batchinator.schedule('First');
|
|
138
|
+
batchinator.schedule('Second');
|
|
139
|
+
batchinator.dispose({ abort: true });
|
|
140
|
+
|
|
141
|
+
expect(fn).not.toHaveBeenCalled();
|
|
142
|
+
|
|
143
|
+
vi.advanceTimersByTime(100);
|
|
144
|
+
expect(fn).not.toHaveBeenCalled();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('Batchinator inSchedule', () => {
|
|
148
|
+
const fn = vi.fn();
|
|
149
|
+
const batchinator = new Batchinator(fn, 100);
|
|
150
|
+
|
|
151
|
+
expect(batchinator.inSchedule()).toBe(false);
|
|
152
|
+
|
|
153
|
+
batchinator.schedule('First');
|
|
154
|
+
expect(batchinator.inSchedule()).toBe(true);
|
|
155
|
+
|
|
156
|
+
batchinator.flush();
|
|
157
|
+
expect(batchinator.inSchedule()).toBe(false);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('Batchinator inSchedule after dispose', () => {
|
|
161
|
+
const fn = vi.fn();
|
|
162
|
+
const batchinator = new Batchinator(fn, 100);
|
|
163
|
+
|
|
164
|
+
batchinator.schedule('First');
|
|
165
|
+
expect(batchinator.inSchedule()).toBe(true);
|
|
166
|
+
|
|
167
|
+
batchinator.dispose({ abort: true });
|
|
168
|
+
expect(batchinator.inSchedule()).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('Batchinator with zero delay', () => {
|
|
172
|
+
const fn = vi.fn();
|
|
173
|
+
const batchinator = new Batchinator(fn, 0, { leading: true });
|
|
174
|
+
|
|
175
|
+
batchinator.schedule('First');
|
|
176
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
177
|
+
expect(fn).toHaveBeenCalledWith('First');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('Batchinator with zero delay and trailing: true', () => {
|
|
181
|
+
const fn = vi.fn();
|
|
182
|
+
const batchinator = new Batchinator(fn, 0, {
|
|
183
|
+
leading: false,
|
|
184
|
+
trailing: true,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
batchinator.schedule('First');
|
|
188
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
189
|
+
expect(fn).toHaveBeenCalledWith('First');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('Batchinator preserves this context', () => {
|
|
193
|
+
const obj = {
|
|
194
|
+
value: 42,
|
|
195
|
+
fn: function (this: any, arg: number) {
|
|
196
|
+
return this.value + arg;
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const batchinator = new Batchinator(obj.fn, 100);
|
|
201
|
+
batchinator.schedule(10);
|
|
202
|
+
|
|
203
|
+
vi.advanceTimersByTime(100);
|
|
204
|
+
// Function executes with correct context
|
|
205
|
+
expect(obj.value).toBe(42);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('Batchinator multiple rapid calls', () => {
|
|
209
|
+
const fn = vi.fn();
|
|
210
|
+
const batchinator = new Batchinator(fn, 100);
|
|
211
|
+
|
|
212
|
+
for (let i = 0; i < 10; i++) {
|
|
213
|
+
batchinator.schedule(i);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
expect(fn).not.toHaveBeenCalled();
|
|
217
|
+
|
|
218
|
+
vi.advanceTimersByTime(100);
|
|
219
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
220
|
+
expect(fn).toHaveBeenCalledWith(9); // Last call
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('Batchinator with multiple arguments', () => {
|
|
224
|
+
const fn = vi.fn();
|
|
225
|
+
const batchinator = new Batchinator(fn, 100);
|
|
226
|
+
|
|
227
|
+
batchinator.schedule(1, 'a', true);
|
|
228
|
+
batchinator.schedule(2, 'b', false);
|
|
229
|
+
batchinator.schedule(3, 'c', true);
|
|
230
|
+
|
|
231
|
+
vi.advanceTimersByTime(100);
|
|
232
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
233
|
+
expect(fn).toHaveBeenCalledWith(3, 'c', true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('Batchinator schedule after flush', () => {
|
|
237
|
+
const fn = vi.fn();
|
|
238
|
+
const batchinator = new Batchinator(fn, 100);
|
|
239
|
+
|
|
240
|
+
batchinator.schedule('First');
|
|
241
|
+
batchinator.flush();
|
|
242
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
243
|
+
|
|
244
|
+
batchinator.schedule('Second');
|
|
245
|
+
vi.advanceTimersByTime(100);
|
|
246
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
247
|
+
expect(fn).toHaveBeenCalledWith('Second');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('Batchinator schedule after dispose', () => {
|
|
251
|
+
const fn = vi.fn();
|
|
252
|
+
const batchinator = new Batchinator(fn, 100);
|
|
253
|
+
|
|
254
|
+
batchinator.schedule('First');
|
|
255
|
+
batchinator.dispose({ abort: true });
|
|
256
|
+
expect(fn).not.toHaveBeenCalled();
|
|
257
|
+
|
|
258
|
+
batchinator.schedule('Second');
|
|
259
|
+
vi.advanceTimersByTime(100);
|
|
260
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
261
|
+
expect(fn).toHaveBeenCalledWith('Second');
|
|
262
|
+
});
|