@x-oasis/batchinate-last 0.1.36 → 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.
@@ -1,21 +1,15 @@
1
1
 
2
- > @x-oasis/batchinate-last@0.1.36 build /home/runner/work/x-oasis/x-oasis/packages/schedule/batchinate-last
2
+ > @x-oasis/batchinate-last@0.1.38 build /home/runner/work/x-oasis/x-oasis/packages/schedule/batchinate-last
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
-  Building modules
9
- ✓ Creating entry file 4.7 secs
10
- ⠹ Building modules
8
+  Creating entry file 2.4 secs
9
+ Building modules
10
+ ⠹ Building modules
11
11
  ⠸ Building modules
12
- ⠼ Building modules
13
- ⠴ Building modules
14
- ⠦ Building modules
15
- ⠧ Building modules
16
- ⠇ Building modules
17
12
  [tsdx]: Your rootDir is currently set to "./". Please change your rootDir to "./src".
18
13
  TSDX has deprecated setting tsconfig.compilerOptions.rootDir to "./" as it caused buggy output for declarationMaps and more.
19
14
  You may also need to change your include to remove "test", which also caused declarations to be unnecessarily created for test files.
20
-  Building modules
21
- ✓ Building modules 9.8 secs
15
+  Building modules 4 secs
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @x-oasis/batchinate-last
2
2
 
3
+ ## 0.1.38
4
+
5
+ ### Patch Changes
6
+
7
+ - f1aae14: bump version
8
+ - Updated dependencies [f1aae14]
9
+ - @x-oasis/debounce@0.1.38
10
+
11
+ ## 0.1.37
12
+
13
+ ### Patch Changes
14
+
15
+ - 8cb524c: trigger next
16
+
3
17
  ## 0.1.36
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -1,19 +1,284 @@
1
1
  # @x-oasis/batchinate-last
2
2
 
3
+ Executes the callback with the last arguments after a delay. If `schedule` is called multiple times within the delay period, only the last call's arguments will be used when the callback executes.
4
+
5
+ This is useful for scenarios where you want to batch multiple rapid calls and only process the final state.
6
+
3
7
  ## Installation
4
8
 
5
9
  ```bash
6
- $ npm i @x-oasis/batchinate-last
10
+ npm install @x-oasis/batchinate-last
11
+ # or
12
+ pnpm add @x-oasis/batchinate-last
13
+ # or
14
+ yarn add @x-oasis/batchinate-last
7
15
  ```
8
16
 
9
- ## How to use
17
+ ## Usage
18
+
19
+ ### Basic Usage
10
20
 
11
21
  ```typescript
12
- import Batchinator from '@x-oasis/batchinate-last'
22
+ import BatchinateLast from '@x-oasis/batchinate-last';
23
+
24
+ const callback = (message: string) => {
25
+ console.log(message);
26
+ };
27
+
28
+ const batchinator = new BatchinateLast(callback, 100);
29
+
30
+ // Schedule multiple calls
31
+ batchinator.schedule('First');
32
+ batchinator.schedule('Second');
33
+ batchinator.schedule('Third');
34
+
35
+ // Only 'Third' will execute after 100ms
13
36
  ```
14
37
 
15
- ## How to run test
38
+ ### Flush Scheduled Task
16
39
 
17
- ```bash
18
- $ pnpm test
19
- ```
40
+ Immediately execute the scheduled task:
41
+
42
+ ```typescript
43
+ import BatchinateLast from '@x-oasis/batchinate-last';
44
+
45
+ const batchinator = new BatchinateLast(callback, 100);
46
+
47
+ batchinator.schedule('First');
48
+ batchinator.schedule('Second');
49
+ batchinator.flush(); // Executes immediately with 'Second'
50
+
51
+ // Or flush with new arguments
52
+ batchinator.flush('Custom');
53
+ ```
54
+
55
+ ### Dispose
56
+
57
+ Cancel the scheduled task and optionally execute it:
58
+
59
+ ```typescript
60
+ import BatchinateLast from '@x-oasis/batchinate-last';
61
+
62
+ const batchinator = new BatchinateLast(callback, 100);
63
+
64
+ batchinator.schedule('First');
65
+ batchinator.schedule('Second');
66
+
67
+ // Dispose and execute
68
+ batchinator.dispose(); // Executes with 'Second'
69
+
70
+ // Dispose without executing
71
+ batchinator.dispose({ abort: true }); // Cancels without executing
72
+ ```
73
+
74
+ ### Check Schedule Status
75
+
76
+ Check if a task is currently scheduled:
77
+
78
+ ```typescript
79
+ import BatchinateLast from '@x-oasis/batchinate-last';
80
+
81
+ const batchinator = new BatchinateLast(callback, 100);
82
+
83
+ batchinator.schedule('First');
84
+ console.log(batchinator.inSchedule()); // true
85
+
86
+ // After execution or cancel
87
+ batchinator.flush();
88
+ console.log(batchinator.inSchedule()); // false
89
+ ```
90
+
91
+ ### Continuous Scheduling
92
+
93
+ If new calls are made during execution, the handler will automatically reschedule:
94
+
95
+ ```typescript
96
+ import BatchinateLast from '@x-oasis/batchinate-last';
97
+
98
+ const batchinator = new BatchinateLast(callback, 100);
99
+
100
+ batchinator.schedule('First');
101
+ // ... 50ms later
102
+ batchinator.schedule('Second');
103
+ // ... 50ms later (during execution)
104
+ batchinator.schedule('Third');
105
+ // Handler will reschedule to execute 'Third'
106
+ ```
107
+
108
+ ## API
109
+
110
+ ### `new BatchinateLast(callback, delayMS)`
111
+
112
+ Creates a new BatchinateLast instance.
113
+
114
+ #### Parameters
115
+
116
+ - `callback` (`Function`): The function to execute with the last arguments.
117
+ - `delayMS` (`number`): The delay in milliseconds before executing the callback.
118
+
119
+ #### Returns
120
+
121
+ Returns a new `BatchinateLast` instance.
122
+
123
+ ### Instance Methods
124
+
125
+ #### `schedule(...args)`
126
+
127
+ Schedule the callback to execute after `delayMS`. If called multiple times, only the last call's arguments will be used.
128
+
129
+ - `args` (`...any[]`): Arguments to pass to the callback.
130
+
131
+ #### `flush(...args)`
132
+
133
+ Immediately execute the scheduled task. If arguments are provided, they will be used instead of stored arguments.
134
+
135
+ - `args` (`...any[]`, optional): Optional arguments to use instead of stored args.
136
+
137
+ #### `dispose(options?)`
138
+
139
+ Dispose the scheduled task. By default, executes the callback with stored arguments unless `abort` is `true`.
140
+
141
+ - `options` (`Object`, optional): Configuration options.
142
+ - `abort` (`boolean`, default: `false`): If `true`, cancel without executing callback.
143
+
144
+ #### `inSchedule()`
145
+
146
+ Check if a task is currently scheduled.
147
+
148
+ - Returns: `boolean` - `true` if a task is scheduled, `false` otherwise.
149
+
150
+ ## Examples
151
+
152
+ ### Search Input
153
+
154
+ ```typescript
155
+ import BatchinateLast from '@x-oasis/batchinate-last';
156
+
157
+ const performSearch = (query: string) => {
158
+ // Perform search with query
159
+ console.log('Searching for:', query);
160
+ };
161
+
162
+ const batchinator = new BatchinateLast(performSearch, 300);
163
+
164
+ // User types in search box
165
+ input.addEventListener('input', (e) => {
166
+ batchinator.schedule(e.target.value);
167
+ });
168
+
169
+ // Only the final query executes after user stops typing
170
+ ```
171
+
172
+ ### Window Resize Handler
173
+
174
+ ```typescript
175
+ import BatchinateLast from '@x-oasis/batchinate-last';
176
+
177
+ const handleResize = (width: number, height: number) => {
178
+ // Recalculate layout
179
+ console.log('Resized to:', width, height);
180
+ };
181
+
182
+ const batchinator = new BatchinateLast(handleResize, 200);
183
+
184
+ window.addEventListener('resize', () => {
185
+ batchinator.schedule(window.innerWidth, window.innerHeight);
186
+ });
187
+
188
+ // Only the final dimensions are processed
189
+ ```
190
+
191
+ ### Form Auto-save
192
+
193
+ ```typescript
194
+ import BatchinateLast from '@x-oasis/batchinate-last';
195
+
196
+ const saveForm = (formData: any) => {
197
+ // Save form data
198
+ console.log('Saving form:', formData);
199
+ };
200
+
201
+ const batchinator = new BatchinateLast(saveForm, 1000);
202
+
203
+ form.addEventListener('input', () => {
204
+ const formData = new FormData(form);
205
+ batchinator.schedule(Object.fromEntries(formData));
206
+ });
207
+
208
+ // Form saves 1 second after user stops editing
209
+ ```
210
+
211
+ ### Scroll Position Tracking
212
+
213
+ ```typescript
214
+ import BatchinateLast from '@x-oasis/batchinate-last';
215
+
216
+ const updateScrollPosition = (x: number, y: number) => {
217
+ // Update scroll position in state
218
+ console.log('Scroll position:', x, y);
219
+ };
220
+
221
+ const batchinator = new BatchinateLast(updateScrollPosition, 100);
222
+
223
+ window.addEventListener('scroll', () => {
224
+ batchinator.schedule(window.scrollX, window.scrollY);
225
+ });
226
+
227
+ // Only the final scroll position is tracked
228
+ ```
229
+
230
+ ### Cleanup on Component Unmount
231
+
232
+ ```typescript
233
+ import BatchinateLast from '@x-oasis/batchinate-last';
234
+ import { useEffect } from 'react';
235
+
236
+ function MyComponent() {
237
+ const batchinator = new BatchinateLast(handleUpdate, 100);
238
+
239
+ useEffect(() => {
240
+ // Use batchinator
241
+ batchinator.schedule(data);
242
+
243
+ // Cleanup on unmount
244
+ return () => {
245
+ batchinator.dispose({ abort: true });
246
+ };
247
+ }, []);
248
+ }
249
+ ```
250
+
251
+ ## Differences from Other Utilities
252
+
253
+ ### vs Debounce
254
+
255
+ - **BatchinateLast**: Always uses the last arguments, executes after a fixed delay.
256
+ - **Debounce**: Resets the delay timer on each call, executes only after a period of inactivity.
257
+
258
+ ### vs Throttle
259
+
260
+ - **BatchinateLast**: Executes once with the last arguments after the delay.
261
+ - **Throttle**: Executes at most once per period, regardless of call frequency.
262
+
263
+ ### vs Batchinator
264
+
265
+ - **BatchinateLast**: Simpler, always executes with last arguments after delay.
266
+ - **Batchinator**: More configurable, supports leading/trailing execution options.
267
+
268
+ ## When to Use BatchinateLast
269
+
270
+ - Search input debouncing
271
+ - Window resize handlers
272
+ - Form auto-save
273
+ - Scroll position tracking
274
+ - Any scenario where you want to batch rapid calls and only process the final state
275
+
276
+ ## See Also
277
+
278
+ - [@x-oasis/batchinator](../batchinator/README.md) - Batches with leading/trailing options
279
+ - [@x-oasis/debounce](../debounce/README.md) - Creates a debounced function
280
+ - [@x-oasis/throttle](../throttle/README.md) - Creates a throttled function
281
+
282
+ ## License
283
+
284
+ ISC
@@ -2,13 +2,50 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
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
+
5
9
  var BatchinateLast = /*#__PURE__*/function () {
6
10
  function BatchinateLast(cb, delayMS) {
11
+ var _this = this;
12
+ this._storedArgs = null;
13
+ this._isScheduled = false;
14
+ this._clockTime = 0;
15
+ this._lastTime = 0;
16
+ this._rescheduleHandler = null;
7
17
  this._callback = cb;
8
18
  this._delayMS = delayMS;
9
- this._taskHandler = null;
10
- this._args = null;
11
- this.handler = this.handler.bind(this);
19
+ var executeAndReschedule = function executeAndReschedule() {
20
+ var savedClockTime = _this._clockTime;
21
+ _this._isScheduled = false;
22
+ if (_this._rescheduleHandler) {
23
+ _this._rescheduleHandler.cancel();
24
+ _this._rescheduleHandler = null;
25
+ }
26
+ if (_this._storedArgs !== null) {
27
+ _this._callback.apply(_this, _this._storedArgs);
28
+ }
29
+ if (_this._delayMS && savedClockTime !== _this._lastTime) {
30
+ var now = Date.now();
31
+ var elapsedTime = now - _this._lastTime;
32
+ var timeoutTime = Math.max(_this._delayMS - elapsedTime, 0);
33
+ _this._clockTime = now;
34
+ var timeoutHandle = setTimeout(function () {
35
+ executeAndReschedule();
36
+ }, timeoutTime);
37
+ _this._rescheduleHandler = {
38
+ cancel: function cancel() {
39
+ return clearTimeout(timeoutHandle);
40
+ }
41
+ };
42
+ _this._isScheduled = true;
43
+ }
44
+ };
45
+ this._debounced = debounce(executeAndReschedule, delayMS, {
46
+ leading: false,
47
+ trailing: true
48
+ });
12
49
  }
13
50
  var _proto = BatchinateLast.prototype;
14
51
  _proto.dispose = function dispose(options) {
@@ -18,67 +55,58 @@ var BatchinateLast = /*#__PURE__*/function () {
18
55
  };
19
56
  }
20
57
  var _options = options,
21
- abort = _options.abort;
22
- if (this._taskHandler) {
23
- this._taskHandler.cancel();
24
- this._taskHandler = null;
25
- }
26
- if (typeof this._callback === 'function' && !abort) {
27
- this._callback.apply(this, this._args);
58
+ _options$abort = _options.abort,
59
+ abort = _options$abort === void 0 ? false : _options$abort;
60
+ if (abort) {
61
+ this._debounced.cancel();
62
+ if (this._rescheduleHandler) {
63
+ this._rescheduleHandler.cancel();
64
+ this._rescheduleHandler = null;
65
+ }
66
+ this._isScheduled = false;
67
+ this._storedArgs = null;
68
+ } else {
69
+ if (this._storedArgs !== null) {
70
+ this._debounced.flush();
71
+ }
28
72
  }
29
73
  };
30
74
  _proto.inSchedule = function inSchedule() {
31
- return !!this._taskHandler;
75
+ return this._isScheduled || this._rescheduleHandler !== null;
32
76
  };
33
77
  _proto.flush = function flush() {
34
78
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
35
79
  args[_key] = arguments[_key];
36
80
  }
37
- if (args.length) this._args = args;
38
- };
39
- _proto.handler = function handler() {
40
- var _this = this;
41
- if (this._taskHandler) {
42
- this._taskHandler.cancel();
43
- this._taskHandler = null;
81
+ if (args.length > 0) {
82
+ this._storedArgs = args;
44
83
  }
45
- this._callback.apply(this, this._args);
46
- if (this._delayMS && this._clockTime !== this._lastTime) {
47
- var elapsedTime = Date.now() - this._lastTime;
48
- var timeoutTime = Math.max(this._delayMS - elapsedTime, 0);
49
- this._clockTime = Date.now();
50
- var timeoutHandler = setTimeout(function () {
51
- _this.handler();
52
- }, timeoutTime);
53
- this._taskHandler = {
54
- cancel: function cancel() {
55
- return clearTimeout(timeoutHandler);
56
- }
57
- };
84
+ this._debounced.flush();
85
+ if (this._rescheduleHandler) {
86
+ this._rescheduleHandler.cancel();
87
+ this._rescheduleHandler = null;
58
88
  }
89
+ this._isScheduled = false;
59
90
  };
60
91
  _proto.schedule = function schedule() {
61
- var _this2 = this;
62
92
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
63
93
  args[_key2] = arguments[_key2];
64
94
  }
65
- this._args = args;
95
+ this._storedArgs = args;
66
96
  var now = Date.now();
67
97
  this._lastTime = now;
68
- if (this._taskHandler) return;
69
98
  if (!this._delayMS) {
70
- this.handler();
99
+ if (this._storedArgs !== null) {
100
+ this._callback.apply(this, this._storedArgs);
101
+ }
71
102
  return;
72
103
  }
104
+ if (this._isScheduled) {
105
+ return;
106
+ }
107
+ this._isScheduled = true;
73
108
  this._clockTime = now;
74
- var timeoutHandler = setTimeout(function () {
75
- _this2.handler();
76
- }, this._delayMS);
77
- this._taskHandler = {
78
- cancel: function cancel() {
79
- return clearTimeout(timeoutHandler);
80
- }
81
- };
109
+ this._debounced();
82
110
  };
83
111
  return BatchinateLast;
84
112
  }();
@@ -1 +1 @@
1
- {"version":3,"file":"batchinate-last.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["class BatchinateLast {\n readonly _delayMS: number;\n private _args: Array<any>;\n\n private _callback: Function;\n\n private _clockTime: number;\n private _lastTime: number;\n private _taskHandler: {\n cancel: () => void;\n };\n\n constructor(cb: Function, delayMS: number) {\n this._callback = cb;\n this._delayMS = delayMS;\n this._taskHandler = null;\n this._args = null;\n this.handler = this.handler.bind(this);\n }\n\n dispose(\n options: {\n abort: boolean;\n } = {\n abort: false,\n }\n ) {\n const { abort } = options;\n if (this._taskHandler) {\n this._taskHandler.cancel();\n this._taskHandler = null;\n }\n if (typeof this._callback === 'function' && !abort) {\n this._callback.apply(this, this._args); // eslint-disable-line\n }\n }\n\n inSchedule() {\n return !!this._taskHandler;\n }\n\n flush(...args) {\n if (args.length) this._args = args;\n }\n\n handler() {\n if (this._taskHandler) {\n this._taskHandler.cancel();\n this._taskHandler = null;\n }\n this._callback.apply(this, this._args); // eslint-disable-line\n\n if (this._delayMS && this._clockTime !== this._lastTime) {\n const elapsedTime = Date.now() - this._lastTime;\n const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);\n this._clockTime = Date.now();\n const timeoutHandler = setTimeout(() => {\n this.handler();\n }, timeoutTime);\n\n this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };\n }\n }\n\n schedule(...args) {\n this._args = args;\n const now = Date.now();\n this._lastTime = now;\n\n if (this._taskHandler) return;\n\n if (!this._delayMS) {\n this.handler();\n return;\n }\n\n this._clockTime = now;\n const timeoutHandler = setTimeout(() => {\n this.handler();\n }, this._delayMS);\n\n this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };\n }\n}\n\nexport default BatchinateLast;\n"],"names":["BatchinateLast","cb","delayMS","_callback","_delayMS","_taskHandler","_args","handler","bind","_proto","prototype","dispose","options","abort","_options","cancel","apply","inSchedule","flush","args","Array","_len","_key","arguments","length","_clockTime","_lastTime","elapsedTime","Date","now","timeoutTime","Math","max","timeoutHandler","setTimeout","_this","clearTimeout","schedule","_len2","_key2","_this2"],"mappings":";;;;IAAMA,cAAc;EAYlB,SAAAA,eAAYC,EAAY,EAAEC,OAAe;IACvC,IAAI,CAACC,SAAS,GAAGF,EAAE;IACnB,IAAI,CAACG,QAAQ,GAAGF,OAAO;IACvB,IAAI,CAACG,YAAY,GAAG,IAAI;IACxB,IAAI,CAACC,KAAK,GAAG,IAAI;IACjB,IAAI,CAACC,OAAO,GAAG,IAAI,CAACA,OAAO,CAACC,IAAI,CAAC,IAAI,CAAC;;EACvC,IAAAC,MAAA,GAAAT,cAAA,CAAAU,SAAA;EAAAD,MAAA,CAEDE,OAAO,GAAP,SAAAA,QACEC;QAAAA;MAAAA,UAEI;QACFC,KAAK,EAAE;OACR;;IAED,IAAAC,QAAA,GAAkBF,OAAO;MAAjBC,KAAK,GAAAC,QAAA,CAALD,KAAK;IACb,IAAI,IAAI,CAACR,YAAY,EAAE;MACrB,IAAI,CAACA,YAAY,CAACU,MAAM,EAAE;MAC1B,IAAI,CAACV,YAAY,GAAG,IAAI;;IAE1B,IAAI,OAAO,IAAI,CAACF,SAAS,KAAK,UAAU,IAAI,CAACU,KAAK,EAAE;MAClD,IAAI,CAACV,SAAS,CAACa,KAAK,CAAC,IAAI,EAAE,IAAI,CAACV,KAAK,CAAC;;GAEzC;EAAAG,MAAA,CAEDQ,UAAU,GAAV,SAAAA;IACE,OAAO,CAAC,CAAC,IAAI,CAACZ,YAAY;GAC3B;EAAAI,MAAA,CAEDS,KAAK,GAAL,SAAAA;sCAASC,IAAI,OAAAC,KAAA,CAAAC,IAAA,GAAAC,IAAA,MAAAA,IAAA,GAAAD,IAAA,EAAAC,IAAA;MAAJH,IAAI,CAAAG,IAAA,IAAAC,SAAA,CAAAD,IAAA;;IACX,IAAIH,IAAI,CAACK,MAAM,EAAE,IAAI,CAAClB,KAAK,GAAGa,IAAI;GACnC;EAAAV,MAAA,CAEDF,OAAO,GAAP,SAAAA;;IACE,IAAI,IAAI,CAACF,YAAY,EAAE;MACrB,IAAI,CAACA,YAAY,CAACU,MAAM,EAAE;MAC1B,IAAI,CAACV,YAAY,GAAG,IAAI;;IAE1B,IAAI,CAACF,SAAS,CAACa,KAAK,CAAC,IAAI,EAAE,IAAI,CAACV,KAAK,CAAC;IAEtC,IAAI,IAAI,CAACF,QAAQ,IAAI,IAAI,CAACqB,UAAU,KAAK,IAAI,CAACC,SAAS,EAAE;MACvD,IAAMC,WAAW,GAAGC,IAAI,CAACC,GAAG,EAAE,GAAG,IAAI,CAACH,SAAS;MAC/C,IAAMI,WAAW,GAAGC,IAAI,CAACC,GAAG,CAAC,IAAI,CAAC5B,QAAQ,GAAGuB,WAAW,EAAE,CAAC,CAAC;MAC5D,IAAI,CAACF,UAAU,GAAGG,IAAI,CAACC,GAAG,EAAE;MAC5B,IAAMI,cAAc,GAAGC,UAAU,CAAC;QAChCC,KAAI,CAAC5B,OAAO,EAAE;OACf,EAAEuB,WAAW,CAAC;MAEf,IAAI,CAACzB,YAAY,GAAG;QAAEU,MAAM,EAAE,SAAAA;UAAA,OAAMqB,YAAY,CAACH,cAAc,CAAC;;OAAE;;GAErE;EAAAxB,MAAA,CAED4B,QAAQ,GAAR,SAAAA;;uCAAYlB,IAAI,OAAAC,KAAA,CAAAkB,KAAA,GAAAC,KAAA,MAAAA,KAAA,GAAAD,KAAA,EAAAC,KAAA;MAAJpB,IAAI,CAAAoB,KAAA,IAAAhB,SAAA,CAAAgB,KAAA;;IACd,IAAI,CAACjC,KAAK,GAAGa,IAAI;IACjB,IAAMU,GAAG,GAAGD,IAAI,CAACC,GAAG,EAAE;IACtB,IAAI,CAACH,SAAS,GAAGG,GAAG;IAEpB,IAAI,IAAI,CAACxB,YAAY,EAAE;IAEvB,IAAI,CAAC,IAAI,CAACD,QAAQ,EAAE;MAClB,IAAI,CAACG,OAAO,EAAE;MACd;;IAGF,IAAI,CAACkB,UAAU,GAAGI,GAAG;IACrB,IAAMI,cAAc,GAAGC,UAAU,CAAC;MAChCM,MAAI,CAACjC,OAAO,EAAE;KACf,EAAE,IAAI,CAACH,QAAQ,CAAC;IAEjB,IAAI,CAACC,YAAY,GAAG;MAAEU,MAAM,EAAE,SAAAA;QAAA,OAAMqB,YAAY,CAACH,cAAc,CAAC;;KAAE;GACnE;EAAA,OAAAjC,cAAA;AAAA;;;;"}
1
+ {"version":3,"file":"batchinate-last.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\n\n/**\n * BatchinateLast - Executes the callback with the last arguments after a delay.\n * If schedule is called multiple times within the delay period, only the last\n * call's arguments will be used when the callback executes.\n */\ntype TaskHandler = {\n cancel: () => void;\n};\n\nclass BatchinateLast {\n readonly _delayMS: number;\n private _callback: (...args: any[]) => void;\n private _debounced: ReturnType<typeof debounce>;\n private _storedArgs: any[] | null = null;\n private _isScheduled = false;\n private _clockTime = 0;\n private _lastTime = 0;\n private _rescheduleHandler: TaskHandler | null = null;\n\n constructor(cb: (...args: any[]) => void, delayMS: number) {\n this._callback = cb;\n this._delayMS = delayMS;\n\n // Helper function to handle execution and potential rescheduling\n const executeAndReschedule = (): void => {\n const savedClockTime = this._clockTime;\n this._isScheduled = false;\n if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\n\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n\n // Check if there were new calls during execution\n // If lastTime was updated (clockTime !== lastTime), reschedule\n if (this._delayMS && savedClockTime !== this._lastTime) {\n const now = Date.now();\n const elapsedTime = now - this._lastTime;\n const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);\n this._clockTime = now;\n\n // Reschedule with remaining time\n const timeoutHandle = setTimeout(() => {\n executeAndReschedule();\n }, timeoutTime);\n this._rescheduleHandler = { cancel: () => clearTimeout(timeoutHandle) };\n this._isScheduled = true;\n }\n };\n\n // Create a debounced function\n // Special behavior: if new calls occur during execution, reschedule\n this._debounced = debounce(executeAndReschedule, delayMS, {\n leading: false,\n trailing: true,\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 if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\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 || this._rescheduleHandler !== null;\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 if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\n this._isScheduled = false;\n }\n\n /**\n * Schedule the callback to execute after delayMS\n * If called multiple times, only the last call's arguments will be used\n * @param args - Arguments to pass to the callback\n */\n schedule(...args: any[]): void {\n this._storedArgs = args;\n const now = Date.now();\n this._lastTime = now;\n\n // Handle zero delay case - execute immediately\n if (!this._delayMS) {\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n return;\n }\n\n // If already scheduled, just update args and return (don't reset timer)\n if (this._isScheduled) {\n return;\n }\n\n // First call - mark as scheduled and call debounce\n this._isScheduled = true;\n this._clockTime = now;\n this._debounced();\n }\n}\n\nexport default BatchinateLast;\n"],"names":["BatchinateLast","cb","delayMS","_callback","_delayMS","executeAndReschedule","savedClockTime","_this","_clockTime","_isScheduled","_rescheduleHandler","cancel","_storedArgs","apply","_lastTime","now","Date","elapsedTime","timeoutTime","Math","max","timeoutHandle","setTimeout","clearTimeout","_debounced","debounce","leading","trailing","_proto","prototype","dispose","options","abort","_options","_options$abort","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}),exports.default=function(){function t(t,a){this._callback=t,this._delayMS=a,this._taskHandler=null,this._args=null,this.handler=this.handler.bind(this)}var a=t.prototype;return a.dispose=function(t){void 0===t&&(t={abort:!1});var a=t.abort;this._taskHandler&&(this._taskHandler.cancel(),this._taskHandler=null),"function"!=typeof this._callback||a||this._callback.apply(this,this._args)},a.inSchedule=function(){return!!this._taskHandler},a.flush=function(){for(var t=arguments.length,a=new Array(t),e=0;e<t;e++)a[e]=arguments[e];a.length&&(this._args=a)},a.handler=function(){var t=this;if(this._taskHandler&&(this._taskHandler.cancel(),this._taskHandler=null),this._callback.apply(this,this._args),this._delayMS&&this._clockTime!==this._lastTime){var a=Date.now()-this._lastTime,e=Math.max(this._delayMS-a,0);this._clockTime=Date.now();var i=setTimeout((function(){t.handler()}),e);this._taskHandler={cancel:function(){return clearTimeout(i)}}}},a.schedule=function(){for(var t=this,a=arguments.length,e=new Array(a),i=0;i<a;i++)e[i]=arguments[i];this._args=e;var s=Date.now();if(this._lastTime=s,!this._taskHandler)if(this._delayMS){this._clockTime=s;var n=setTimeout((function(){t.handler()}),this._delayMS);this._taskHandler={cancel:function(){return clearTimeout(n)}}}else this.handler()},t}();
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,l=(e=require("@x-oasis/debounce"))&&"object"==typeof e&&"default"in e?e.default:e;exports.default=function(){function e(e,s){var t=this;this._storedArgs=null,this._isScheduled=!1,this._clockTime=0,this._lastTime=0,this._rescheduleHandler=null,this._callback=e,this._delayMS=s,this._debounced=l((function e(){var l=t._clockTime;if(t._isScheduled=!1,t._rescheduleHandler&&(t._rescheduleHandler.cancel(),t._rescheduleHandler=null),null!==t._storedArgs&&t._callback.apply(t,t._storedArgs),t._delayMS&&l!==t._lastTime){var s=Date.now(),i=Math.max(t._delayMS-(s-t._lastTime),0);t._clockTime=s;var r=setTimeout((function(){e()}),i);t._rescheduleHandler={cancel:function(){return clearTimeout(r)}},t._isScheduled=!0}}),s,{leading:!1,trailing:!0})}var s=e.prototype;return s.dispose=function(e){void 0===e&&(e={abort:!1});var l=e.abort;void 0!==l&&l?(this._debounced.cancel(),this._rescheduleHandler&&(this._rescheduleHandler.cancel(),this._rescheduleHandler=null),this._isScheduled=!1,this._storedArgs=null):null!==this._storedArgs&&this._debounced.flush()},s.inSchedule=function(){return this._isScheduled||null!==this._rescheduleHandler},s.flush=function(){for(var e=arguments.length,l=new Array(e),s=0;s<e;s++)l[s]=arguments[s];l.length>0&&(this._storedArgs=l),this._debounced.flush(),this._rescheduleHandler&&(this._rescheduleHandler.cancel(),this._rescheduleHandler=null),this._isScheduled=!1},s.schedule=function(){for(var e=arguments.length,l=new Array(e),s=0;s<e;s++)l[s]=arguments[s];this._storedArgs=l;var t=Date.now();this._lastTime=t,this._delayMS?this._isScheduled||(this._isScheduled=!0,this._clockTime=t,this._debounced()):null!==this._storedArgs&&this._callback.apply(this,this._storedArgs)},e}();
2
2
  //# sourceMappingURL=batchinate-last.cjs.production.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"batchinate-last.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["class BatchinateLast {\n readonly _delayMS: number;\n private _args: Array<any>;\n\n private _callback: Function;\n\n private _clockTime: number;\n private _lastTime: number;\n private _taskHandler: {\n cancel: () => void;\n };\n\n constructor(cb: Function, delayMS: number) {\n this._callback = cb;\n this._delayMS = delayMS;\n this._taskHandler = null;\n this._args = null;\n this.handler = this.handler.bind(this);\n }\n\n dispose(\n options: {\n abort: boolean;\n } = {\n abort: false,\n }\n ) {\n const { abort } = options;\n if (this._taskHandler) {\n this._taskHandler.cancel();\n this._taskHandler = null;\n }\n if (typeof this._callback === 'function' && !abort) {\n this._callback.apply(this, this._args); // eslint-disable-line\n }\n }\n\n inSchedule() {\n return !!this._taskHandler;\n }\n\n flush(...args) {\n if (args.length) this._args = args;\n }\n\n handler() {\n if (this._taskHandler) {\n this._taskHandler.cancel();\n this._taskHandler = null;\n }\n this._callback.apply(this, this._args); // eslint-disable-line\n\n if (this._delayMS && this._clockTime !== this._lastTime) {\n const elapsedTime = Date.now() - this._lastTime;\n const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);\n this._clockTime = Date.now();\n const timeoutHandler = setTimeout(() => {\n this.handler();\n }, timeoutTime);\n\n this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };\n }\n }\n\n schedule(...args) {\n this._args = args;\n const now = Date.now();\n this._lastTime = now;\n\n if (this._taskHandler) return;\n\n if (!this._delayMS) {\n this.handler();\n return;\n }\n\n this._clockTime = now;\n const timeoutHandler = setTimeout(() => {\n this.handler();\n }, this._delayMS);\n\n this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };\n }\n}\n\nexport default BatchinateLast;\n"],"names":["BatchinateLast","cb","delayMS","this","_callback","_delayMS","_taskHandler","_args","handler","bind","_proto","prototype","dispose","options","abort","cancel","apply","inSchedule","flush","args","Array","_len","_key","arguments","length","_clockTime","_lastTime","elapsedTime","Date","now","timeoutTime","Math","max","timeoutHandler","setTimeout","_this","clearTimeout","schedule","_len2","_key2","_this2"],"mappings":"+FAYE,SAAAA,EAAYC,EAAcC,GACxBC,KAAKC,UAAYH,EACjBE,KAAKE,SAAWH,EAChBC,KAAKG,aAAe,KACpBH,KAAKI,MAAQ,KACbJ,KAAKK,QAAUL,KAAKK,QAAQC,KAAKN,MAClC,IAAAO,EAAAV,EAAAW,UAgEA,OAhEAD,EAEDE,QAAA,SACEC,YAAAA,IAAAA,EAEI,CACFC,OAAO,IAGT,IAAQA,EAAUD,EAAVC,MACJX,KAAKG,eACPH,KAAKG,aAAaS,SAClBZ,KAAKG,aAAe,MAEQ,mBAAnBH,KAAKC,WAA6BU,GAC3CX,KAAKC,UAAUY,MAAMb,KAAMA,KAAKI,QAEnCG,EAEDO,WAAA,WACE,QAASd,KAAKG,cACfI,EAEDQ,MAAA,sCAASC,MAAIC,MAAAC,GAAAC,IAAAA,EAAAD,EAAAC,IAAJH,EAAIG,GAAAC,UAAAD,GACPH,EAAKK,SAAQrB,KAAKI,MAAQY,IAC/BT,EAEDF,QAAA,sBAOE,GANIL,KAAKG,eACPH,KAAKG,aAAaS,SAClBZ,KAAKG,aAAe,MAEtBH,KAAKC,UAAUY,MAAMb,KAAMA,KAAKI,OAE5BJ,KAAKE,UAAYF,KAAKsB,aAAetB,KAAKuB,UAAW,CACvD,IAAMC,EAAcC,KAAKC,MAAQ1B,KAAKuB,UAChCI,EAAcC,KAAKC,IAAI7B,KAAKE,SAAWsB,EAAa,GAC1DxB,KAAKsB,WAAaG,KAAKC,MACvB,IAAMI,EAAiBC,YAAW,WAChCC,EAAK3B,YACJsB,GAEH3B,KAAKG,aAAe,CAAES,OAAQ,WAAA,OAAMqB,aAAaH,OAEpDvB,EAED2B,SAAA,6CAAYlB,MAAIC,MAAAkB,GAAAC,IAAAA,EAAAD,EAAAC,IAAJpB,EAAIoB,GAAAhB,UAAAgB,GACdpC,KAAKI,MAAQY,EACb,IAAMU,EAAMD,KAAKC,MAGjB,GAFA1B,KAAKuB,UAAYG,GAEb1B,KAAKG,aAET,GAAKH,KAAKE,SAAV,CAKAF,KAAKsB,WAAaI,EAClB,IAAMI,EAAiBC,YAAW,WAChCM,EAAKhC,YACJL,KAAKE,UAERF,KAAKG,aAAe,CAAES,OAAQ,WAAA,OAAMqB,aAAaH,UAT/C9B,KAAKK,WAURR"}
1
+ {"version":3,"file":"batchinate-last.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\n\n/**\n * BatchinateLast - Executes the callback with the last arguments after a delay.\n * If schedule is called multiple times within the delay period, only the last\n * call's arguments will be used when the callback executes.\n */\ntype TaskHandler = {\n cancel: () => void;\n};\n\nclass BatchinateLast {\n readonly _delayMS: number;\n private _callback: (...args: any[]) => void;\n private _debounced: ReturnType<typeof debounce>;\n private _storedArgs: any[] | null = null;\n private _isScheduled = false;\n private _clockTime = 0;\n private _lastTime = 0;\n private _rescheduleHandler: TaskHandler | null = null;\n\n constructor(cb: (...args: any[]) => void, delayMS: number) {\n this._callback = cb;\n this._delayMS = delayMS;\n\n // Helper function to handle execution and potential rescheduling\n const executeAndReschedule = (): void => {\n const savedClockTime = this._clockTime;\n this._isScheduled = false;\n if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\n\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n\n // Check if there were new calls during execution\n // If lastTime was updated (clockTime !== lastTime), reschedule\n if (this._delayMS && savedClockTime !== this._lastTime) {\n const now = Date.now();\n const elapsedTime = now - this._lastTime;\n const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);\n this._clockTime = now;\n\n // Reschedule with remaining time\n const timeoutHandle = setTimeout(() => {\n executeAndReschedule();\n }, timeoutTime);\n this._rescheduleHandler = { cancel: () => clearTimeout(timeoutHandle) };\n this._isScheduled = true;\n }\n };\n\n // Create a debounced function\n // Special behavior: if new calls occur during execution, reschedule\n this._debounced = debounce(executeAndReschedule, delayMS, {\n leading: false,\n trailing: true,\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 if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\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 || this._rescheduleHandler !== null;\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 if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\n this._isScheduled = false;\n }\n\n /**\n * Schedule the callback to execute after delayMS\n * If called multiple times, only the last call's arguments will be used\n * @param args - Arguments to pass to the callback\n */\n schedule(...args: any[]): void {\n this._storedArgs = args;\n const now = Date.now();\n this._lastTime = now;\n\n // Handle zero delay case - execute immediately\n if (!this._delayMS) {\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n return;\n }\n\n // If already scheduled, just update args and return (don't reset timer)\n if (this._isScheduled) {\n return;\n }\n\n // First call - mark as scheduled and call debounce\n this._isScheduled = true;\n this._clockTime = now;\n this._debounced();\n }\n}\n\nexport default BatchinateLast;\n"],"names":["BatchinateLast","cb","delayMS","this","_callback","_delayMS","_debounced","debounce","executeAndReschedule","savedClockTime","_this","_clockTime","_isScheduled","_rescheduleHandler","cancel","_storedArgs","apply","_lastTime","now","Date","timeoutTime","Math","max","timeoutHandle","setTimeout","clearTimeout","leading","trailing","_proto","prototype","dispose","options","abort","_options$abort","flush","inSchedule","args","Array","_len","_key","arguments","length","schedule","_len2","_key2"],"mappings":"uLAqBE,SAAAA,EAAYC,EAA8BC,cANlCC,iBAA4B,KAC5BA,mBAAe,EACfA,gBAAa,EACbA,eAAY,EACZA,wBAAyC,KAG/CA,KAAKC,UAAYH,EACjBE,KAAKE,SAAWH,EAkChBC,KAAKG,WAAaC,GA/BW,SAAvBC,IACJ,IAAMC,EAAiBC,EAAKC,WAa5B,GAZAD,EAAKE,cAAe,EAChBF,EAAKG,qBACPH,EAAKG,mBAAmBC,SACxBJ,EAAKG,mBAAqB,MAGH,OAArBH,EAAKK,aACPL,EAAKN,UAASY,MAAdN,EAAkBA,EAAKK,aAKrBL,EAAKL,UAAYI,IAAmBC,EAAKO,UAAW,CACtD,IAAMC,EAAMC,KAAKD,MAEXE,EAAcC,KAAKC,IAAIZ,EAAKL,UADda,EAAMR,EAAKO,WAC2B,GAC1DP,EAAKC,WAAaO,EAGlB,IAAMK,EAAgBC,YAAW,WAC/BhB,MACCY,GACHV,EAAKG,mBAAqB,CAAEC,OAAQ,WAAA,OAAMW,aAAaF,KACvDb,EAAKE,cAAe,KAMyBV,EAAS,CACxDwB,SAAS,EACTC,UAAU,IAEb,IAAAC,EAAA5B,EAAA6B,UAiFA,OAjFAD,EAODE,QAAA,SACEC,YAAAA,IAAAA,EAEI,CACFC,OAAO,IAGT,IAAiCC,EAAPF,EAAlBC,eAAKC,GAAQA,GAEnB9B,KAAKG,WAAWQ,SACZX,KAAKU,qBACPV,KAAKU,mBAAmBC,SACxBX,KAAKU,mBAAqB,MAE5BV,KAAKS,cAAe,EACpBT,KAAKY,YAAc,MAGM,OAArBZ,KAAKY,aACPZ,KAAKG,WAAW4B,SAGrBN,EAKDO,WAAA,WACE,OAAOhC,KAAKS,cAA4C,OAA5BT,KAAKU,oBAClCe,EAMDM,MAAA,sCAASE,MAAWC,MAAAC,GAAAC,IAAAA,EAAAD,EAAAC,IAAXH,EAAWG,GAAAC,UAAAD,GACdH,EAAKK,OAAS,IAChBtC,KAAKY,YAAcqB,GAErBjC,KAAKG,WAAW4B,QACZ/B,KAAKU,qBACPV,KAAKU,mBAAmBC,SACxBX,KAAKU,mBAAqB,MAE5BV,KAAKS,cAAe,GACrBgB,EAODc,SAAA,sCAAYN,MAAWC,MAAAM,GAAAC,IAAAA,EAAAD,EAAAC,IAAXR,EAAWQ,GAAAJ,UAAAI,GACrBzC,KAAKY,YAAcqB,EACnB,IAAMlB,EAAMC,KAAKD,MACjBf,KAAKc,UAAYC,EAGZf,KAAKE,SAQNF,KAAKS,eAKTT,KAAKS,cAAe,EACpBT,KAAKQ,WAAaO,EAClBf,KAAKG,cAdsB,OAArBH,KAAKY,aACPZ,KAAKC,UAASY,MAAdb,KAAkBA,KAAKY,cAc5Bf"}
@@ -1,10 +1,45 @@
1
+ import debounce from '@x-oasis/debounce';
2
+
1
3
  var BatchinateLast = /*#__PURE__*/function () {
2
4
  function BatchinateLast(cb, delayMS) {
5
+ var _this = this;
6
+ this._storedArgs = null;
7
+ this._isScheduled = false;
8
+ this._clockTime = 0;
9
+ this._lastTime = 0;
10
+ this._rescheduleHandler = null;
3
11
  this._callback = cb;
4
12
  this._delayMS = delayMS;
5
- this._taskHandler = null;
6
- this._args = null;
7
- this.handler = this.handler.bind(this);
13
+ var executeAndReschedule = function executeAndReschedule() {
14
+ var savedClockTime = _this._clockTime;
15
+ _this._isScheduled = false;
16
+ if (_this._rescheduleHandler) {
17
+ _this._rescheduleHandler.cancel();
18
+ _this._rescheduleHandler = null;
19
+ }
20
+ if (_this._storedArgs !== null) {
21
+ _this._callback.apply(_this, _this._storedArgs);
22
+ }
23
+ if (_this._delayMS && savedClockTime !== _this._lastTime) {
24
+ var now = Date.now();
25
+ var elapsedTime = now - _this._lastTime;
26
+ var timeoutTime = Math.max(_this._delayMS - elapsedTime, 0);
27
+ _this._clockTime = now;
28
+ var timeoutHandle = setTimeout(function () {
29
+ executeAndReschedule();
30
+ }, timeoutTime);
31
+ _this._rescheduleHandler = {
32
+ cancel: function cancel() {
33
+ return clearTimeout(timeoutHandle);
34
+ }
35
+ };
36
+ _this._isScheduled = true;
37
+ }
38
+ };
39
+ this._debounced = debounce(executeAndReschedule, delayMS, {
40
+ leading: false,
41
+ trailing: true
42
+ });
8
43
  }
9
44
  var _proto = BatchinateLast.prototype;
10
45
  _proto.dispose = function dispose(options) {
@@ -14,67 +49,58 @@ var BatchinateLast = /*#__PURE__*/function () {
14
49
  };
15
50
  }
16
51
  var _options = options,
17
- abort = _options.abort;
18
- if (this._taskHandler) {
19
- this._taskHandler.cancel();
20
- this._taskHandler = null;
21
- }
22
- if (typeof this._callback === 'function' && !abort) {
23
- this._callback.apply(this, this._args);
52
+ _options$abort = _options.abort,
53
+ abort = _options$abort === void 0 ? false : _options$abort;
54
+ if (abort) {
55
+ this._debounced.cancel();
56
+ if (this._rescheduleHandler) {
57
+ this._rescheduleHandler.cancel();
58
+ this._rescheduleHandler = null;
59
+ }
60
+ this._isScheduled = false;
61
+ this._storedArgs = null;
62
+ } else {
63
+ if (this._storedArgs !== null) {
64
+ this._debounced.flush();
65
+ }
24
66
  }
25
67
  };
26
68
  _proto.inSchedule = function inSchedule() {
27
- return !!this._taskHandler;
69
+ return this._isScheduled || this._rescheduleHandler !== null;
28
70
  };
29
71
  _proto.flush = function flush() {
30
72
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
31
73
  args[_key] = arguments[_key];
32
74
  }
33
- if (args.length) this._args = args;
34
- };
35
- _proto.handler = function handler() {
36
- var _this = this;
37
- if (this._taskHandler) {
38
- this._taskHandler.cancel();
39
- this._taskHandler = null;
75
+ if (args.length > 0) {
76
+ this._storedArgs = args;
40
77
  }
41
- this._callback.apply(this, this._args);
42
- if (this._delayMS && this._clockTime !== this._lastTime) {
43
- var elapsedTime = Date.now() - this._lastTime;
44
- var timeoutTime = Math.max(this._delayMS - elapsedTime, 0);
45
- this._clockTime = Date.now();
46
- var timeoutHandler = setTimeout(function () {
47
- _this.handler();
48
- }, timeoutTime);
49
- this._taskHandler = {
50
- cancel: function cancel() {
51
- return clearTimeout(timeoutHandler);
52
- }
53
- };
78
+ this._debounced.flush();
79
+ if (this._rescheduleHandler) {
80
+ this._rescheduleHandler.cancel();
81
+ this._rescheduleHandler = null;
54
82
  }
83
+ this._isScheduled = false;
55
84
  };
56
85
  _proto.schedule = function schedule() {
57
- var _this2 = this;
58
86
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
59
87
  args[_key2] = arguments[_key2];
60
88
  }
61
- this._args = args;
89
+ this._storedArgs = args;
62
90
  var now = Date.now();
63
91
  this._lastTime = now;
64
- if (this._taskHandler) return;
65
92
  if (!this._delayMS) {
66
- this.handler();
93
+ if (this._storedArgs !== null) {
94
+ this._callback.apply(this, this._storedArgs);
95
+ }
67
96
  return;
68
97
  }
98
+ if (this._isScheduled) {
99
+ return;
100
+ }
101
+ this._isScheduled = true;
69
102
  this._clockTime = now;
70
- var timeoutHandler = setTimeout(function () {
71
- _this2.handler();
72
- }, this._delayMS);
73
- this._taskHandler = {
74
- cancel: function cancel() {
75
- return clearTimeout(timeoutHandler);
76
- }
77
- };
103
+ this._debounced();
78
104
  };
79
105
  return BatchinateLast;
80
106
  }();
@@ -1 +1 @@
1
- {"version":3,"file":"batchinate-last.esm.js","sources":["../src/index.ts"],"sourcesContent":["class BatchinateLast {\n readonly _delayMS: number;\n private _args: Array<any>;\n\n private _callback: Function;\n\n private _clockTime: number;\n private _lastTime: number;\n private _taskHandler: {\n cancel: () => void;\n };\n\n constructor(cb: Function, delayMS: number) {\n this._callback = cb;\n this._delayMS = delayMS;\n this._taskHandler = null;\n this._args = null;\n this.handler = this.handler.bind(this);\n }\n\n dispose(\n options: {\n abort: boolean;\n } = {\n abort: false,\n }\n ) {\n const { abort } = options;\n if (this._taskHandler) {\n this._taskHandler.cancel();\n this._taskHandler = null;\n }\n if (typeof this._callback === 'function' && !abort) {\n this._callback.apply(this, this._args); // eslint-disable-line\n }\n }\n\n inSchedule() {\n return !!this._taskHandler;\n }\n\n flush(...args) {\n if (args.length) this._args = args;\n }\n\n handler() {\n if (this._taskHandler) {\n this._taskHandler.cancel();\n this._taskHandler = null;\n }\n this._callback.apply(this, this._args); // eslint-disable-line\n\n if (this._delayMS && this._clockTime !== this._lastTime) {\n const elapsedTime = Date.now() - this._lastTime;\n const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);\n this._clockTime = Date.now();\n const timeoutHandler = setTimeout(() => {\n this.handler();\n }, timeoutTime);\n\n this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };\n }\n }\n\n schedule(...args) {\n this._args = args;\n const now = Date.now();\n this._lastTime = now;\n\n if (this._taskHandler) return;\n\n if (!this._delayMS) {\n this.handler();\n return;\n }\n\n this._clockTime = now;\n const timeoutHandler = setTimeout(() => {\n this.handler();\n }, this._delayMS);\n\n this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };\n }\n}\n\nexport default BatchinateLast;\n"],"names":["BatchinateLast","cb","delayMS","_callback","_delayMS","_taskHandler","_args","handler","bind","_proto","prototype","dispose","options","abort","_options","cancel","apply","inSchedule","flush","args","Array","_len","_key","arguments","length","_clockTime","_lastTime","elapsedTime","Date","now","timeoutTime","Math","max","timeoutHandler","setTimeout","_this","clearTimeout","schedule","_len2","_key2","_this2"],"mappings":"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"}
1
+ {"version":3,"file":"batchinate-last.esm.js","sources":["../src/index.ts"],"sourcesContent":["import debounce from '@x-oasis/debounce';\n\n/**\n * BatchinateLast - Executes the callback with the last arguments after a delay.\n * If schedule is called multiple times within the delay period, only the last\n * call's arguments will be used when the callback executes.\n */\ntype TaskHandler = {\n cancel: () => void;\n};\n\nclass BatchinateLast {\n readonly _delayMS: number;\n private _callback: (...args: any[]) => void;\n private _debounced: ReturnType<typeof debounce>;\n private _storedArgs: any[] | null = null;\n private _isScheduled = false;\n private _clockTime = 0;\n private _lastTime = 0;\n private _rescheduleHandler: TaskHandler | null = null;\n\n constructor(cb: (...args: any[]) => void, delayMS: number) {\n this._callback = cb;\n this._delayMS = delayMS;\n\n // Helper function to handle execution and potential rescheduling\n const executeAndReschedule = (): void => {\n const savedClockTime = this._clockTime;\n this._isScheduled = false;\n if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\n\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n\n // Check if there were new calls during execution\n // If lastTime was updated (clockTime !== lastTime), reschedule\n if (this._delayMS && savedClockTime !== this._lastTime) {\n const now = Date.now();\n const elapsedTime = now - this._lastTime;\n const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);\n this._clockTime = now;\n\n // Reschedule with remaining time\n const timeoutHandle = setTimeout(() => {\n executeAndReschedule();\n }, timeoutTime);\n this._rescheduleHandler = { cancel: () => clearTimeout(timeoutHandle) };\n this._isScheduled = true;\n }\n };\n\n // Create a debounced function\n // Special behavior: if new calls occur during execution, reschedule\n this._debounced = debounce(executeAndReschedule, delayMS, {\n leading: false,\n trailing: true,\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 if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\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 || this._rescheduleHandler !== null;\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 if (this._rescheduleHandler) {\n this._rescheduleHandler.cancel();\n this._rescheduleHandler = null;\n }\n this._isScheduled = false;\n }\n\n /**\n * Schedule the callback to execute after delayMS\n * If called multiple times, only the last call's arguments will be used\n * @param args - Arguments to pass to the callback\n */\n schedule(...args: any[]): void {\n this._storedArgs = args;\n const now = Date.now();\n this._lastTime = now;\n\n // Handle zero delay case - execute immediately\n if (!this._delayMS) {\n if (this._storedArgs !== null) {\n this._callback(...this._storedArgs);\n }\n return;\n }\n\n // If already scheduled, just update args and return (don't reset timer)\n if (this._isScheduled) {\n return;\n }\n\n // First call - mark as scheduled and call debounce\n this._isScheduled = true;\n this._clockTime = now;\n this._debounced();\n }\n}\n\nexport default BatchinateLast;\n"],"names":["BatchinateLast","cb","delayMS","_callback","_delayMS","executeAndReschedule","savedClockTime","_this","_clockTime","_isScheduled","_rescheduleHandler","cancel","_storedArgs","apply","_lastTime","now","Date","elapsedTime","timeoutTime","Math","max","timeoutHandle","setTimeout","clearTimeout","_debounced","debounce","leading","trailing","_proto","prototype","dispose","options","abort","_options","_options$abort","flush","inSchedule","args","Array","_len","_key","arguments","length","schedule","_len2","_key2"],"mappings":";;AAAyC,IAWnCA,cAAc;EAUlB,SAAAA,eAAYC,EAA4B,EAAEC,OAAe;;IANjD,gBAAW,GAAiB,IAAI;IAChC,iBAAY,GAAG,KAAK;IACpB,eAAU,GAAG,CAAC;IACd,cAAS,GAAG,CAAC;IACb,uBAAkB,GAAuB,IAAI;IAGnD,IAAI,CAACC,SAAS,GAAGF,EAAE;IACnB,IAAI,CAACG,QAAQ,GAAGF,OAAO;IAGvB,IAAMG,oBAAoB,GAAG,SAAvBA,oBAAoBA;MACxB,IAAMC,cAAc,GAAGC,KAAI,CAACC,UAAU;MACtCD,KAAI,CAACE,YAAY,GAAG,KAAK;MACzB,IAAIF,KAAI,CAACG,kBAAkB,EAAE;QAC3BH,KAAI,CAACG,kBAAkB,CAACC,MAAM,EAAE;QAChCJ,KAAI,CAACG,kBAAkB,GAAG,IAAI;;MAGhC,IAAIH,KAAI,CAACK,WAAW,KAAK,IAAI,EAAE;QAC7BL,KAAI,CAACJ,SAAS,CAAAU,KAAA,CAAdN,KAAI,EAAcA,KAAI,CAACK,WAAW,CAAC;;MAKrC,IAAIL,KAAI,CAACH,QAAQ,IAAIE,cAAc,KAAKC,KAAI,CAACO,SAAS,EAAE;QACtD,IAAMC,GAAG,GAAGC,IAAI,CAACD,GAAG,EAAE;QACtB,IAAME,WAAW,GAAGF,GAAG,GAAGR,KAAI,CAACO,SAAS;QACxC,IAAMI,WAAW,GAAGC,IAAI,CAACC,GAAG,CAACb,KAAI,CAACH,QAAQ,GAAGa,WAAW,EAAE,CAAC,CAAC;QAC5DV,KAAI,CAACC,UAAU,GAAGO,GAAG;QAGrB,IAAMM,aAAa,GAAGC,UAAU,CAAC;UAC/BjB,oBAAoB,EAAE;SACvB,EAAEa,WAAW,CAAC;QACfX,KAAI,CAACG,kBAAkB,GAAG;UAAEC,MAAM,EAAE,SAAAA;YAAA,OAAMY,YAAY,CAACF,aAAa,CAAC;;SAAE;QACvEd,KAAI,CAACE,YAAY,GAAG,IAAI;;KAE3B;IAID,IAAI,CAACe,UAAU,GAAGC,QAAQ,CAACpB,oBAAoB,EAAEH,OAAO,EAAE;MACxDwB,OAAO,EAAE,KAAK;MACdC,QAAQ,EAAE;KACX,CAAC;;EACH,IAAAC,MAAA,GAAA5B,cAAA,CAAA6B,SAAA;EAAAD,MAAA,CAODE,OAAO,GAAP,SAAAA,QACEC;QAAAA;MAAAA,UAEI;QACFC,KAAK,EAAE;OACR;;IAED,IAAAC,QAAA,GAA0BF,OAAO;MAAAG,cAAA,GAAAD,QAAA,CAAzBD,KAAK;MAALA,KAAK,GAAAE,cAAA,cAAG,KAAK,GAAAA,cAAA;IACrB,IAAIF,KAAK,EAAE;MACT,IAAI,CAACR,UAAU,CAACb,MAAM,EAAE;MACxB,IAAI,IAAI,CAACD,kBAAkB,EAAE;QAC3B,IAAI,CAACA,kBAAkB,CAACC,MAAM,EAAE;QAChC,IAAI,CAACD,kBAAkB,GAAG,IAAI;;MAEhC,IAAI,CAACD,YAAY,GAAG,KAAK;MACzB,IAAI,CAACG,WAAW,GAAG,IAAI;KACxB,MAAM;MAEL,IAAI,IAAI,CAACA,WAAW,KAAK,IAAI,EAAE;QAC7B,IAAI,CAACY,UAAU,CAACW,KAAK,EAAE;;;GAG5B;EAAAP,MAAA,CAKDQ,UAAU,GAAV,SAAAA;IACE,OAAO,IAAI,CAAC3B,YAAY,IAAI,IAAI,CAACC,kBAAkB,KAAK,IAAI;GAC7D;EAAAkB,MAAA,CAMDO,KAAK,GAAL,SAAAA;sCAASE,IAAW,OAAAC,KAAA,CAAAC,IAAA,GAAAC,IAAA,MAAAA,IAAA,GAAAD,IAAA,EAAAC,IAAA;MAAXH,IAAW,CAAAG,IAAA,IAAAC,SAAA,CAAAD,IAAA;;IAClB,IAAIH,IAAI,CAACK,MAAM,GAAG,CAAC,EAAE;MACnB,IAAI,CAAC9B,WAAW,GAAGyB,IAAI;;IAEzB,IAAI,CAACb,UAAU,CAACW,KAAK,EAAE;IACvB,IAAI,IAAI,CAACzB,kBAAkB,EAAE;MAC3B,IAAI,CAACA,kBAAkB,CAACC,MAAM,EAAE;MAChC,IAAI,CAACD,kBAAkB,GAAG,IAAI;;IAEhC,IAAI,CAACD,YAAY,GAAG,KAAK;GAC1B;EAAAmB,MAAA,CAODe,QAAQ,GAAR,SAAAA;uCAAYN,IAAW,OAAAC,KAAA,CAAAM,KAAA,GAAAC,KAAA,MAAAA,KAAA,GAAAD,KAAA,EAAAC,KAAA;MAAXR,IAAW,CAAAQ,KAAA,IAAAJ,SAAA,CAAAI,KAAA;;IACrB,IAAI,CAACjC,WAAW,GAAGyB,IAAI;IACvB,IAAMtB,GAAG,GAAGC,IAAI,CAACD,GAAG,EAAE;IACtB,IAAI,CAACD,SAAS,GAAGC,GAAG;IAGpB,IAAI,CAAC,IAAI,CAACX,QAAQ,EAAE;MAClB,IAAI,IAAI,CAACQ,WAAW,KAAK,IAAI,EAAE;QAC7B,IAAI,CAACT,SAAS,CAAAU,KAAA,CAAd,IAAI,EAAc,IAAI,CAACD,WAAW,CAAC;;MAErC;;IAIF,IAAI,IAAI,CAACH,YAAY,EAAE;MACrB;;IAIF,IAAI,CAACA,YAAY,GAAG,IAAI;IACxB,IAAI,CAACD,UAAU,GAAGO,GAAG;IACrB,IAAI,CAACS,UAAU,EAAE;GAClB;EAAA,OAAAxB,cAAA;AAAA;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  declare class BatchinateLast {
2
2
  readonly _delayMS: number;
3
- private _args;
4
3
  private _callback;
4
+ private _debounced;
5
+ private _storedArgs;
6
+ private _isScheduled;
5
7
  private _clockTime;
6
8
  private _lastTime;
7
- private _taskHandler;
8
- constructor(cb: Function, delayMS: number);
9
+ private _rescheduleHandler;
10
+ constructor(cb: (...args: any[]) => void, delayMS: number);
9
11
  dispose(options?: {
10
- abort: boolean;
12
+ abort?: boolean;
11
13
  }): void;
12
14
  inSchedule(): boolean;
13
15
  flush(...args: any[]): void;
14
- handler(): void;
15
16
  schedule(...args: any[]): void;
16
17
  }
17
18
  export default BatchinateLast;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-oasis/batchinate-last",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "description": "batchinate-last function",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -13,6 +13,9 @@
13
13
  "devDependencies": {
14
14
  "tsdx": "^0.14.1"
15
15
  },
16
+ "dependencies": {
17
+ "@x-oasis/debounce": "0.1.38"
18
+ },
16
19
  "scripts": {
17
20
  "build": "tsdx build --tsconfig tsconfig.build.json",
18
21
  "clean": "rimraf ./dist",
package/src/index.ts CHANGED
@@ -1,85 +1,145 @@
1
- class BatchinateLast {
2
- readonly _delayMS: number;
3
- private _args: Array<any>;
1
+ import debounce from '@x-oasis/debounce';
4
2
 
5
- private _callback: Function;
3
+ /**
4
+ * BatchinateLast - Executes the callback with the last arguments after a delay.
5
+ * If schedule is called multiple times within the delay period, only the last
6
+ * call's arguments will be used when the callback executes.
7
+ */
8
+ type TaskHandler = {
9
+ cancel: () => void;
10
+ };
6
11
 
7
- private _clockTime: number;
8
- private _lastTime: number;
9
- private _taskHandler: {
10
- cancel: () => void;
11
- };
12
+ class BatchinateLast {
13
+ readonly _delayMS: number;
14
+ private _callback: (...args: any[]) => void;
15
+ private _debounced: ReturnType<typeof debounce>;
16
+ private _storedArgs: any[] | null = null;
17
+ private _isScheduled = false;
18
+ private _clockTime = 0;
19
+ private _lastTime = 0;
20
+ private _rescheduleHandler: TaskHandler | null = null;
12
21
 
13
- constructor(cb: Function, delayMS: number) {
22
+ constructor(cb: (...args: any[]) => void, delayMS: number) {
14
23
  this._callback = cb;
15
24
  this._delayMS = delayMS;
16
- this._taskHandler = null;
17
- this._args = null;
18
- this.handler = this.handler.bind(this);
25
+
26
+ // Helper function to handle execution and potential rescheduling
27
+ const executeAndReschedule = (): void => {
28
+ const savedClockTime = this._clockTime;
29
+ this._isScheduled = false;
30
+ if (this._rescheduleHandler) {
31
+ this._rescheduleHandler.cancel();
32
+ this._rescheduleHandler = null;
33
+ }
34
+
35
+ if (this._storedArgs !== null) {
36
+ this._callback(...this._storedArgs);
37
+ }
38
+
39
+ // Check if there were new calls during execution
40
+ // If lastTime was updated (clockTime !== lastTime), reschedule
41
+ if (this._delayMS && savedClockTime !== this._lastTime) {
42
+ const now = Date.now();
43
+ const elapsedTime = now - this._lastTime;
44
+ const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);
45
+ this._clockTime = now;
46
+
47
+ // Reschedule with remaining time
48
+ const timeoutHandle = setTimeout(() => {
49
+ executeAndReschedule();
50
+ }, timeoutTime);
51
+ this._rescheduleHandler = { cancel: () => clearTimeout(timeoutHandle) };
52
+ this._isScheduled = true;
53
+ }
54
+ };
55
+
56
+ // Create a debounced function
57
+ // Special behavior: if new calls occur during execution, reschedule
58
+ this._debounced = debounce(executeAndReschedule, delayMS, {
59
+ leading: false,
60
+ trailing: true,
61
+ });
19
62
  }
20
63
 
64
+ /**
65
+ * Dispose the scheduled task
66
+ * @param options - Configuration options
67
+ * @param options.abort - If true, cancel without executing callback
68
+ */
21
69
  dispose(
22
70
  options: {
23
- abort: boolean;
71
+ abort?: boolean;
24
72
  } = {
25
73
  abort: false,
26
74
  }
27
- ) {
28
- const { abort } = options;
29
- if (this._taskHandler) {
30
- this._taskHandler.cancel();
31
- this._taskHandler = null;
32
- }
33
- if (typeof this._callback === 'function' && !abort) {
34
- this._callback.apply(this, this._args); // eslint-disable-line
75
+ ): void {
76
+ const { abort = false } = options;
77
+ if (abort) {
78
+ this._debounced.cancel();
79
+ if (this._rescheduleHandler) {
80
+ this._rescheduleHandler.cancel();
81
+ this._rescheduleHandler = null;
82
+ }
83
+ this._isScheduled = false;
84
+ this._storedArgs = null;
85
+ } else {
86
+ // Execute with current args if any
87
+ if (this._storedArgs !== null) {
88
+ this._debounced.flush();
89
+ }
35
90
  }
36
91
  }
37
92
 
38
- inSchedule() {
39
- return !!this._taskHandler;
93
+ /**
94
+ * Check if a task is currently scheduled
95
+ */
96
+ inSchedule(): boolean {
97
+ return this._isScheduled || this._rescheduleHandler !== null;
40
98
  }
41
99
 
42
- flush(...args) {
43
- if (args.length) this._args = args;
44
- }
45
-
46
- handler() {
47
- if (this._taskHandler) {
48
- this._taskHandler.cancel();
49
- this._taskHandler = null;
100
+ /**
101
+ * Flush the scheduled task immediately
102
+ * @param args - Optional arguments to use instead of stored args
103
+ */
104
+ flush(...args: any[]): void {
105
+ if (args.length > 0) {
106
+ this._storedArgs = args;
50
107
  }
51
- this._callback.apply(this, this._args); // eslint-disable-line
52
-
53
- if (this._delayMS && this._clockTime !== this._lastTime) {
54
- const elapsedTime = Date.now() - this._lastTime;
55
- const timeoutTime = Math.max(this._delayMS - elapsedTime, 0);
56
- this._clockTime = Date.now();
57
- const timeoutHandler = setTimeout(() => {
58
- this.handler();
59
- }, timeoutTime);
60
-
61
- this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };
108
+ this._debounced.flush();
109
+ if (this._rescheduleHandler) {
110
+ this._rescheduleHandler.cancel();
111
+ this._rescheduleHandler = null;
62
112
  }
113
+ this._isScheduled = false;
63
114
  }
64
115
 
65
- schedule(...args) {
66
- this._args = args;
116
+ /**
117
+ * Schedule the callback to execute after delayMS
118
+ * If called multiple times, only the last call's arguments will be used
119
+ * @param args - Arguments to pass to the callback
120
+ */
121
+ schedule(...args: any[]): void {
122
+ this._storedArgs = args;
67
123
  const now = Date.now();
68
124
  this._lastTime = now;
69
125
 
70
- if (this._taskHandler) return;
71
-
126
+ // Handle zero delay case - execute immediately
72
127
  if (!this._delayMS) {
73
- this.handler();
128
+ if (this._storedArgs !== null) {
129
+ this._callback(...this._storedArgs);
130
+ }
74
131
  return;
75
132
  }
76
133
 
77
- this._clockTime = now;
78
- const timeoutHandler = setTimeout(() => {
79
- this.handler();
80
- }, this._delayMS);
134
+ // If already scheduled, just update args and return (don't reset timer)
135
+ if (this._isScheduled) {
136
+ return;
137
+ }
81
138
 
82
- this._taskHandler = { cancel: () => clearTimeout(timeoutHandler) };
139
+ // First call - mark as scheduled and call debounce
140
+ this._isScheduled = true;
141
+ this._clockTime = now;
142
+ this._debounced();
83
143
  }
84
144
  }
85
145
 
package/test/test.spec.ts CHANGED
@@ -1,5 +1,249 @@
1
- import { expect, test } from 'vitest';
1
+ import { expect, test, vi, beforeEach, afterEach } from 'vitest';
2
+ import BatchinateLast from '../src/index';
2
3
 
3
- test('vitest', async () => {
4
- expect('vitest').toBe('vitest');
4
+ beforeEach(() => {
5
+ vi.useFakeTimers();
6
+ });
7
+
8
+ afterEach(() => {
9
+ vi.restoreAllMocks();
10
+ });
11
+
12
+ test('BatchinateLast basic functionality - uses last arguments', () => {
13
+ const fn = vi.fn();
14
+ const batchinator = new BatchinateLast(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('BatchinateLast flush', () => {
29
+ const fn = vi.fn();
30
+ const batchinator = new BatchinateLast(fn, 100);
31
+
32
+ batchinator.schedule('First');
33
+ batchinator.schedule('Second');
34
+ batchinator.flush();
35
+
36
+ expect(fn).toHaveBeenCalledTimes(1);
37
+ expect(fn).toHaveBeenCalledWith('Second');
38
+
39
+ vi.advanceTimersByTime(100);
40
+ expect(fn).toHaveBeenCalledTimes(1); // Already flushed
41
+ });
42
+
43
+ test('BatchinateLast flush with new arguments', () => {
44
+ const fn = vi.fn();
45
+ const batchinator = new BatchinateLast(fn, 100);
46
+
47
+ batchinator.schedule('First');
48
+ batchinator.flush('Custom');
49
+
50
+ expect(fn).toHaveBeenCalledTimes(1);
51
+ expect(fn).toHaveBeenCalledWith('Custom');
52
+ });
53
+
54
+ test('BatchinateLast dispose without abort', () => {
55
+ const fn = vi.fn();
56
+ const batchinator = new BatchinateLast(fn, 100);
57
+
58
+ batchinator.schedule('First');
59
+ batchinator.schedule('Second');
60
+ batchinator.dispose();
61
+
62
+ expect(fn).toHaveBeenCalledTimes(1);
63
+ expect(fn).toHaveBeenCalledWith('Second');
64
+
65
+ vi.advanceTimersByTime(100);
66
+ expect(fn).toHaveBeenCalledTimes(1); // Already disposed
67
+ });
68
+
69
+ test('BatchinateLast dispose with abort', () => {
70
+ const fn = vi.fn();
71
+ const batchinator = new BatchinateLast(fn, 100);
72
+
73
+ batchinator.schedule('First');
74
+ batchinator.schedule('Second');
75
+ batchinator.dispose({ abort: true });
76
+
77
+ expect(fn).not.toHaveBeenCalled();
78
+
79
+ vi.advanceTimersByTime(100);
80
+ expect(fn).not.toHaveBeenCalled();
81
+ });
82
+
83
+ test('BatchinateLast inSchedule', () => {
84
+ const fn = vi.fn();
85
+ const batchinator = new BatchinateLast(fn, 100);
86
+
87
+ expect(batchinator.inSchedule()).toBe(false);
88
+
89
+ batchinator.schedule('First');
90
+ expect(batchinator.inSchedule()).toBe(true);
91
+
92
+ batchinator.flush();
93
+ expect(batchinator.inSchedule()).toBe(false);
94
+ });
95
+
96
+ test('BatchinateLast inSchedule after dispose', () => {
97
+ const fn = vi.fn();
98
+ const batchinator = new BatchinateLast(fn, 100);
99
+
100
+ batchinator.schedule('First');
101
+ expect(batchinator.inSchedule()).toBe(true);
102
+
103
+ batchinator.dispose({ abort: true });
104
+ expect(batchinator.inSchedule()).toBe(false);
105
+ });
106
+
107
+ test('BatchinateLast with zero delay', () => {
108
+ const fn = vi.fn();
109
+ const batchinator = new BatchinateLast(fn, 0);
110
+
111
+ batchinator.schedule('First');
112
+ expect(fn).toHaveBeenCalledTimes(1);
113
+ expect(fn).toHaveBeenCalledWith('First');
114
+ });
115
+
116
+ test('BatchinateLast preserves this context', () => {
117
+ const obj = {
118
+ value: 42,
119
+ fn: function (this: any, arg: number) {
120
+ return this.value + arg;
121
+ },
122
+ };
123
+
124
+ const batchinator = new BatchinateLast(obj.fn, 100);
125
+ batchinator.schedule(10);
126
+
127
+ vi.advanceTimersByTime(100);
128
+ // Function executes with correct context
129
+ expect(obj.value).toBe(42);
130
+ });
131
+
132
+ test('BatchinateLast multiple rapid calls', () => {
133
+ const fn = vi.fn();
134
+ const batchinator = new BatchinateLast(fn, 100);
135
+
136
+ for (let i = 0; i < 10; i++) {
137
+ batchinator.schedule(i);
138
+ }
139
+
140
+ expect(fn).not.toHaveBeenCalled();
141
+
142
+ vi.advanceTimersByTime(100);
143
+ expect(fn).toHaveBeenCalledTimes(1);
144
+ expect(fn).toHaveBeenCalledWith(9); // Last call
145
+ });
146
+
147
+ test('BatchinateLast with multiple arguments', () => {
148
+ const fn = vi.fn();
149
+ const batchinator = new BatchinateLast(fn, 100);
150
+
151
+ batchinator.schedule(1, 'a', true);
152
+ batchinator.schedule(2, 'b', false);
153
+ batchinator.schedule(3, 'c', true);
154
+
155
+ vi.advanceTimersByTime(100);
156
+ expect(fn).toHaveBeenCalledTimes(1);
157
+ expect(fn).toHaveBeenCalledWith(3, 'c', true);
158
+ });
159
+
160
+ test('BatchinateLast reschedules if new calls during execution', () => {
161
+ const fn = vi.fn();
162
+ const batchinator = new BatchinateLast(fn, 100);
163
+
164
+ batchinator.schedule('First');
165
+
166
+ // Advance time to trigger execution
167
+ vi.advanceTimersByTime(50);
168
+ batchinator.schedule('Second'); // New call during wait period
169
+
170
+ vi.advanceTimersByTime(50);
171
+ // Handler executes with 'First', then checks for new calls
172
+ expect(fn).toHaveBeenCalled();
173
+
174
+ // If there were new calls, it should reschedule
175
+ vi.advanceTimersByTime(100);
176
+ // Should execute with 'Second'
177
+ expect(fn.mock.calls.length).toBeGreaterThan(1);
178
+ });
179
+
180
+ test('BatchinateLast schedule after flush', () => {
181
+ const fn = vi.fn();
182
+ const batchinator = new BatchinateLast(fn, 100);
183
+
184
+ batchinator.schedule('First');
185
+ batchinator.flush();
186
+ expect(fn).toHaveBeenCalledTimes(1);
187
+
188
+ batchinator.schedule('Second');
189
+ vi.advanceTimersByTime(100);
190
+ expect(fn).toHaveBeenCalledTimes(2);
191
+ expect(fn).toHaveBeenCalledWith('Second');
192
+ });
193
+
194
+ test('BatchinateLast schedule after dispose', () => {
195
+ const fn = vi.fn();
196
+ const batchinator = new BatchinateLast(fn, 100);
197
+
198
+ batchinator.schedule('First');
199
+ batchinator.dispose({ abort: true });
200
+ expect(fn).not.toHaveBeenCalled();
201
+
202
+ batchinator.schedule('Second');
203
+ vi.advanceTimersByTime(100);
204
+ expect(fn).toHaveBeenCalledTimes(1);
205
+ expect(fn).toHaveBeenCalledWith('Second');
206
+ });
207
+
208
+ test('BatchinateLast continuous scheduling', () => {
209
+ const fn = vi.fn();
210
+ const batchinator = new BatchinateLast(fn, 100);
211
+
212
+ batchinator.schedule('First');
213
+ vi.advanceTimersByTime(50);
214
+ batchinator.schedule('Second');
215
+ vi.advanceTimersByTime(50);
216
+ batchinator.schedule('Third');
217
+
218
+ // Should eventually execute with 'Third'
219
+ vi.advanceTimersByTime(100);
220
+ expect(fn).toHaveBeenCalled();
221
+ });
222
+
223
+ test('BatchinateLast flush empty schedule', () => {
224
+ const fn = vi.fn();
225
+ const batchinator = new BatchinateLast(fn, 100);
226
+
227
+ // Flush without scheduling
228
+ batchinator.flush();
229
+ expect(fn).not.toHaveBeenCalled();
230
+ });
231
+
232
+ test('BatchinateLast dispose empty schedule', () => {
233
+ const fn = vi.fn();
234
+ const batchinator = new BatchinateLast(fn, 100);
235
+
236
+ // Dispose without scheduling
237
+ batchinator.dispose();
238
+ expect(fn).not.toHaveBeenCalled();
239
+ });
240
+
241
+ test('BatchinateLast flush with empty args after scheduling', () => {
242
+ const fn = vi.fn();
243
+ const batchinator = new BatchinateLast(fn, 100);
244
+
245
+ batchinator.schedule('First');
246
+ batchinator.flush(); // No new args provided
247
+ expect(fn).toHaveBeenCalledTimes(1);
248
+ expect(fn).toHaveBeenCalledWith('First');
5
249
  });