@tanstack/pacer 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/async-debouncer.cjs +60 -44
- package/dist/cjs/async-debouncer.cjs.map +1 -1
- package/dist/cjs/async-debouncer.d.cts +37 -24
- package/dist/cjs/async-queuer.cjs +149 -125
- package/dist/cjs/async-queuer.cjs.map +1 -1
- package/dist/cjs/async-queuer.d.cts +65 -48
- package/dist/cjs/async-rate-limiter.cjs +63 -46
- package/dist/cjs/async-rate-limiter.cjs.map +1 -1
- package/dist/cjs/async-rate-limiter.d.cts +39 -27
- package/dist/cjs/async-throttler.cjs +70 -47
- package/dist/cjs/async-throttler.cjs.map +1 -1
- package/dist/cjs/async-throttler.d.cts +43 -25
- package/dist/cjs/debouncer.cjs +46 -22
- package/dist/cjs/debouncer.cjs.map +1 -1
- package/dist/cjs/debouncer.d.cts +25 -11
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/queuer.cjs +114 -104
- package/dist/cjs/queuer.cjs.map +1 -1
- package/dist/cjs/queuer.d.cts +53 -40
- package/dist/cjs/rate-limiter.cjs +54 -42
- package/dist/cjs/rate-limiter.cjs.map +1 -1
- package/dist/cjs/rate-limiter.d.cts +37 -45
- package/dist/cjs/throttler.cjs +61 -41
- package/dist/cjs/throttler.cjs.map +1 -1
- package/dist/cjs/throttler.d.cts +35 -22
- package/dist/cjs/types.d.cts +12 -0
- package/dist/cjs/utils.cjs +13 -0
- package/dist/cjs/utils.cjs.map +1 -0
- package/dist/cjs/utils.d.cts +1 -0
- package/dist/esm/async-debouncer.d.ts +37 -24
- package/dist/esm/async-debouncer.js +60 -44
- package/dist/esm/async-debouncer.js.map +1 -1
- package/dist/esm/async-queuer.d.ts +65 -48
- package/dist/esm/async-queuer.js +149 -125
- package/dist/esm/async-queuer.js.map +1 -1
- package/dist/esm/async-rate-limiter.d.ts +39 -27
- package/dist/esm/async-rate-limiter.js +63 -46
- package/dist/esm/async-rate-limiter.js.map +1 -1
- package/dist/esm/async-throttler.d.ts +43 -25
- package/dist/esm/async-throttler.js +70 -47
- package/dist/esm/async-throttler.js.map +1 -1
- package/dist/esm/debouncer.d.ts +25 -11
- package/dist/esm/debouncer.js +46 -22
- package/dist/esm/debouncer.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/queuer.d.ts +53 -40
- package/dist/esm/queuer.js +114 -104
- package/dist/esm/queuer.js.map +1 -1
- package/dist/esm/rate-limiter.d.ts +37 -45
- package/dist/esm/rate-limiter.js +54 -42
- package/dist/esm/rate-limiter.js.map +1 -1
- package/dist/esm/throttler.d.ts +35 -22
- package/dist/esm/throttler.js +61 -41
- package/dist/esm/throttler.js.map +1 -1
- package/dist/esm/types.d.ts +12 -0
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +13 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +8 -1
- package/src/async-debouncer.ts +90 -62
- package/src/async-queuer.ts +178 -145
- package/src/async-rate-limiter.ts +93 -67
- package/src/async-throttler.ts +98 -63
- package/src/debouncer.ts +71 -35
- package/src/index.ts +2 -0
- package/src/queuer.ts +135 -118
- package/src/rate-limiter.ts +79 -81
- package/src/throttler.ts +87 -61
- package/src/types.ts +17 -0
- package/src/utils.ts +13 -0
package/dist/esm/debouncer.js
CHANGED
|
@@ -2,14 +2,17 @@ const defaultOptions = {
|
|
|
2
2
|
enabled: true,
|
|
3
3
|
leading: false,
|
|
4
4
|
trailing: true,
|
|
5
|
-
wait: 0
|
|
5
|
+
wait: 0,
|
|
6
|
+
onExecute: () => {
|
|
7
|
+
}
|
|
6
8
|
};
|
|
7
9
|
class Debouncer {
|
|
8
10
|
constructor(fn, initialOptions) {
|
|
9
11
|
this.fn = fn;
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
12
|
+
this._canLeadingExecute = true;
|
|
13
|
+
this._isPending = false;
|
|
14
|
+
this._executionCount = 0;
|
|
15
|
+
this._options = {
|
|
13
16
|
...defaultOptions,
|
|
14
17
|
...initialOptions
|
|
15
18
|
};
|
|
@@ -19,49 +22,70 @@ class Debouncer {
|
|
|
19
22
|
* Returns the new options state
|
|
20
23
|
*/
|
|
21
24
|
setOptions(newOptions) {
|
|
22
|
-
this.
|
|
23
|
-
...this.
|
|
25
|
+
this._options = {
|
|
26
|
+
...this._options,
|
|
24
27
|
...newOptions
|
|
25
28
|
};
|
|
26
|
-
|
|
29
|
+
if (!this._options.enabled) {
|
|
30
|
+
this._isPending = false;
|
|
31
|
+
}
|
|
32
|
+
return this._options;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
|
-
* Returns the
|
|
35
|
+
* Returns the current debouncer options
|
|
30
36
|
*/
|
|
31
|
-
|
|
32
|
-
return this.
|
|
37
|
+
getOptions() {
|
|
38
|
+
return this._options;
|
|
33
39
|
}
|
|
34
40
|
/**
|
|
35
41
|
* Attempts to execute the debounced function
|
|
36
42
|
* If a call is already in progress, it will be queued
|
|
37
43
|
*/
|
|
38
44
|
maybeExecute(...args) {
|
|
39
|
-
if (this.
|
|
45
|
+
if (this._options.leading && this._canLeadingExecute) {
|
|
40
46
|
this.executeFunction(...args);
|
|
41
|
-
this.
|
|
47
|
+
this._canLeadingExecute = false;
|
|
42
48
|
}
|
|
43
|
-
if (this.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
if (this._options.leading || this._options.trailing) {
|
|
50
|
+
this._isPending = true;
|
|
51
|
+
}
|
|
52
|
+
if (this._timeoutId) clearTimeout(this._timeoutId);
|
|
53
|
+
this._timeoutId = setTimeout(() => {
|
|
54
|
+
this._canLeadingExecute = true;
|
|
55
|
+
this._isPending = false;
|
|
56
|
+
if (this._options.trailing) {
|
|
47
57
|
this.executeFunction(...args);
|
|
48
58
|
}
|
|
49
|
-
}, this.
|
|
59
|
+
}, this._options.wait);
|
|
50
60
|
}
|
|
51
61
|
executeFunction(...args) {
|
|
52
|
-
if (!this.
|
|
53
|
-
this.executionCount++;
|
|
62
|
+
if (!this._options.enabled) return;
|
|
54
63
|
this.fn(...args);
|
|
64
|
+
this._executionCount++;
|
|
65
|
+
this._options.onExecute(this);
|
|
55
66
|
}
|
|
56
67
|
/**
|
|
57
68
|
* Cancels any pending execution
|
|
58
69
|
*/
|
|
59
70
|
cancel() {
|
|
60
|
-
if (this.
|
|
61
|
-
clearTimeout(this.
|
|
62
|
-
this.
|
|
71
|
+
if (this._timeoutId) {
|
|
72
|
+
clearTimeout(this._timeoutId);
|
|
73
|
+
this._canLeadingExecute = true;
|
|
74
|
+
this._isPending = false;
|
|
63
75
|
}
|
|
64
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Returns the number of times the function has been executed
|
|
79
|
+
*/
|
|
80
|
+
getExecutionCount() {
|
|
81
|
+
return this._executionCount;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns `true` if debouncing
|
|
85
|
+
*/
|
|
86
|
+
getIsPending() {
|
|
87
|
+
return this._options.enabled && this._isPending;
|
|
88
|
+
}
|
|
65
89
|
}
|
|
66
90
|
function debounce(fn, initialOptions) {
|
|
67
91
|
const debouncer = new Debouncer(fn, initialOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debouncer.js","sources":["../../src/debouncer.ts"],"sourcesContent":["/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Delay in milliseconds before executing the function\n * Defaults to 0ms\n */\n wait: number\n}\n\nconst defaultOptions: Required<DebouncerOptions
|
|
1
|
+
{"version":3,"file":"debouncer.js","sources":["../../src/debouncer.ts"],"sourcesContent":["import type { AnyFunction } from './types'\n\n/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions<\n TFn extends AnyFunction,\n TArgs extends Parameters<TFn>,\n> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (debouncer: Debouncer<TFn, TArgs>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Delay in milliseconds before executing the function\n * Defaults to 0ms\n */\n wait: number\n}\n\nconst defaultOptions: Required<DebouncerOptions<any, any>> = {\n enabled: true,\n leading: false,\n trailing: true,\n wait: 0,\n onExecute: () => {},\n}\n\n/**\n * A class that creates a debounced function.\n *\n * Debouncing ensures that a function is only executed after a certain amount of time has passed\n * since its last invocation. This is useful for handling frequent events like window resizing,\n * scroll events, or input changes where you want to limit the rate of execution.\n *\n * The debounced function can be configured to execute either at the start of the delay period\n * (leading edge) or at the end (trailing edge, default). Each new call during the wait period\n * will reset the timer.\n *\n * @example\n * ```ts\n * const debouncer = new Debouncer((value: string) => {\n * saveToDatabase(value);\n * }, { wait: 500 });\n *\n * // Will only save after 500ms of no new input\n * inputElement.addEventListener('input', () => {\n * debouncer.maybeExecute(inputElement.value);\n * });\n * ```\n */\nexport class Debouncer<TFn extends AnyFunction, TArgs extends Parameters<TFn>> {\n private _canLeadingExecute = true\n private _isPending = false\n private _executionCount = 0\n private _options: Required<DebouncerOptions<TFn, TArgs>>\n private _timeoutId: NodeJS.Timeout | undefined\n\n constructor(\n private fn: TFn,\n initialOptions: DebouncerOptions<TFn, TArgs>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n }\n }\n\n /**\n * Updates the debouncer options\n * Returns the new options state\n */\n setOptions(\n newOptions: Partial<DebouncerOptions<TFn, TArgs>>,\n ): Required<DebouncerOptions<TFn, TArgs>> {\n this._options = {\n ...this._options,\n ...newOptions,\n }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this._isPending = false\n }\n\n return this._options\n }\n\n /**\n * Returns the current debouncer options\n */\n getOptions(): Required<DebouncerOptions<TFn, TArgs>> {\n return this._options\n }\n\n /**\n * Attempts to execute the debounced function\n * If a call is already in progress, it will be queued\n */\n maybeExecute(...args: TArgs): void {\n // Handle leading execution\n if (this._options.leading && this._canLeadingExecute) {\n this.executeFunction(...args)\n this._canLeadingExecute = false\n }\n\n // Start pending state\n if (this._options.leading || this._options.trailing) {\n this._isPending = true\n }\n\n // Clear any existing timeout\n if (this._timeoutId) clearTimeout(this._timeoutId)\n\n // Set new timeout that will reset canLeadingExecute\n this._timeoutId = setTimeout(() => {\n this._canLeadingExecute = true\n this._isPending = false\n // Execute trailing only if enabled\n if (this._options.trailing) {\n this.executeFunction(...args)\n }\n }, this._options.wait)\n }\n\n private executeFunction(...args: TArgs): void {\n if (!this._options.enabled) return\n this.fn(...args) // EXECUTE!\n this._executionCount++\n this._options.onExecute(this)\n }\n\n /**\n * Cancels any pending execution\n */\n cancel(): void {\n if (this._timeoutId) {\n clearTimeout(this._timeoutId)\n this._canLeadingExecute = true\n this._isPending = false\n }\n }\n\n /**\n * Returns the number of times the function has been executed\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns `true` if debouncing\n */\n getIsPending(): boolean {\n return this._options.enabled && this._isPending\n }\n}\n\n/**\n * Creates a debounced function that delays invoking the provided function until after a specified wait time.\n * Multiple calls during the wait period will cancel previous pending invocations and reset the timer.\n *\n * This the the simple function wrapper implementation pulled from the Debouncer class. If you need\n * more control over the debouncing behavior, use the Debouncer class directly.\n *\n * If leading option is true, the function will execute immediately on the first call, then wait the delay\n * before allowing another execution.\n *\n * @example\n * ```ts\n * const debounced = debounce(() => {\n * saveChanges();\n * }, { wait: 1000 });\n *\n * // Called repeatedly but executes at most once per second\n * inputElement.addEventListener('input', debounced);\n * ```\n */\nexport function debounce<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: Omit<DebouncerOptions<TFn, Parameters<TFn>>, 'enabled'>,\n) {\n const debouncer = new Debouncer(fn, initialOptions)\n return debouncer.maybeExecute.bind(debouncer)\n}\n"],"names":[],"mappings":"AAmCA,MAAM,iBAAuD;AAAA,EAC3D,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,EACN,WAAW,MAAM;AAAA,EAAA;AACnB;AAyBO,MAAM,UAAkE;AAAA,EAO7E,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AAPV,SAAQ,qBAAqB;AAC7B,SAAQ,aAAa;AACrB,SAAQ,kBAAkB;AAQxB,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WACE,YACwC;AACxC,SAAK,WAAW;AAAA,MACd,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,IACL;AAGI,QAAA,CAAC,KAAK,SAAS,SAAS;AAC1B,WAAK,aAAa;AAAA,IAAA;AAGpB,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,aAAqD;AACnD,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,gBAAgB,MAAmB;AAEjC,QAAI,KAAK,SAAS,WAAW,KAAK,oBAAoB;AAC/C,WAAA,gBAAgB,GAAG,IAAI;AAC5B,WAAK,qBAAqB;AAAA,IAAA;AAI5B,QAAI,KAAK,SAAS,WAAW,KAAK,SAAS,UAAU;AACnD,WAAK,aAAa;AAAA,IAAA;AAIpB,QAAI,KAAK,WAAyB,cAAA,KAAK,UAAU;AAG5C,SAAA,aAAa,WAAW,MAAM;AACjC,WAAK,qBAAqB;AAC1B,WAAK,aAAa;AAEd,UAAA,KAAK,SAAS,UAAU;AACrB,aAAA,gBAAgB,GAAG,IAAI;AAAA,MAAA;AAAA,IAC9B,GACC,KAAK,SAAS,IAAI;AAAA,EAAA;AAAA,EAGf,mBAAmB,MAAmB;AACxC,QAAA,CAAC,KAAK,SAAS,QAAS;AACvB,SAAA,GAAG,GAAG,IAAI;AACV,SAAA;AACA,SAAA,SAAS,UAAU,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,SAAe;AACb,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,qBAAqB;AAC1B,WAAK,aAAa;AAAA,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAMF,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,eAAwB;AACf,WAAA,KAAK,SAAS,WAAW,KAAK;AAAA,EAAA;AAEzC;AAsBgB,SAAA,SACd,IACA,gBACA;AACA,QAAM,YAAY,IAAI,UAAU,IAAI,cAAc;AAC3C,SAAA,UAAU,aAAa,KAAK,SAAS;AAC9C;"}
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { Debouncer, debounce } from "./debouncer.js";
|
|
|
7
7
|
import { Queuer, queue } from "./queuer.js";
|
|
8
8
|
import { RateLimiter, rateLimit } from "./rate-limiter.js";
|
|
9
9
|
import { Throttler, throttle } from "./throttler.js";
|
|
10
|
+
import { bindInstanceMethods } from "./utils.js";
|
|
10
11
|
export {
|
|
11
12
|
AsyncDebouncer,
|
|
12
13
|
AsyncQueuer,
|
|
@@ -20,6 +21,7 @@ export {
|
|
|
20
21
|
asyncQueue,
|
|
21
22
|
asyncRateLimit,
|
|
22
23
|
asyncThrottle,
|
|
24
|
+
bindInstanceMethods,
|
|
23
25
|
debounce,
|
|
24
26
|
isPlainArray,
|
|
25
27
|
isPlainObject,
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
package/dist/esm/queuer.d.ts
CHANGED
|
@@ -29,10 +29,18 @@ export interface QueuerOptions<TValue> {
|
|
|
29
29
|
* Callback fired whenever an item is removed from the queuer
|
|
30
30
|
*/
|
|
31
31
|
onGetNextItem?: (item: TValue, queuer: Queuer<TValue>) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Callback fired whenever the queuer's running state changes
|
|
34
|
+
*/
|
|
35
|
+
onIsRunningChange?: (queuer: Queuer<TValue>) => void;
|
|
32
36
|
/**
|
|
33
37
|
* Callback fired whenever an item is added or removed from the queuer
|
|
34
38
|
*/
|
|
35
|
-
|
|
39
|
+
onItemsChange?: (queuer: Queuer<TValue>) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Callback fired whenever an item is rejected from being added to the queuer
|
|
42
|
+
*/
|
|
43
|
+
onReject?: (item: TValue, queuer: Queuer<TValue>) => void;
|
|
36
44
|
/**
|
|
37
45
|
* Whether the queuer should start processing tasks immediately
|
|
38
46
|
*/
|
|
@@ -73,7 +81,7 @@ export type QueuePosition = 'front' | 'back';
|
|
|
73
81
|
* - start(): begins processing items in the queuer
|
|
74
82
|
* - stop(): pauses processing
|
|
75
83
|
* - wait: configurable delay between processing items
|
|
76
|
-
* -
|
|
84
|
+
* - onItemsChange/onGetNextItem: callbacks for monitoring queuer state
|
|
77
85
|
*
|
|
78
86
|
* @example
|
|
79
87
|
* ```ts
|
|
@@ -88,7 +96,7 @@ export type QueuePosition = 'front' | 'back';
|
|
|
88
96
|
* getPriority: (n) => n, // Higher numbers have priority
|
|
89
97
|
* started: true, // Begin processing immediately
|
|
90
98
|
* wait: 1000, // Wait 1s between items
|
|
91
|
-
* onGetNextItem: (item) => console.log(item)
|
|
99
|
+
* onGetNextItem: (item, queuer) => console.log(item)
|
|
92
100
|
* });
|
|
93
101
|
* priorityQueue.addItem(1); // [1]
|
|
94
102
|
* priorityQueue.addItem(3); // [3, 1] - 3 processed first
|
|
@@ -96,22 +104,43 @@ export type QueuePosition = 'front' | 'back';
|
|
|
96
104
|
* ```
|
|
97
105
|
*/
|
|
98
106
|
export declare class Queuer<TValue> {
|
|
99
|
-
|
|
100
|
-
private
|
|
101
|
-
private
|
|
102
|
-
private
|
|
103
|
-
private
|
|
104
|
-
private
|
|
107
|
+
private _options;
|
|
108
|
+
private _items;
|
|
109
|
+
private _executionCount;
|
|
110
|
+
private _rejectionCount;
|
|
111
|
+
private _onItemsChanges;
|
|
112
|
+
private _running;
|
|
113
|
+
private _pendingTick;
|
|
105
114
|
constructor(initialOptions?: QueuerOptions<TValue>);
|
|
106
|
-
/**
|
|
107
|
-
* Processes items in the queuer
|
|
108
|
-
*/
|
|
109
|
-
protected tick(): void;
|
|
110
115
|
/**
|
|
111
116
|
* Updates the queuer options
|
|
112
117
|
* Returns the new options state
|
|
113
118
|
*/
|
|
114
119
|
setOptions(newOptions: Partial<QueuerOptions<TValue>>): QueuerOptions<TValue>;
|
|
120
|
+
/**
|
|
121
|
+
* Returns the current queuer options
|
|
122
|
+
*/
|
|
123
|
+
getOptions(): Required<QueuerOptions<TValue>>;
|
|
124
|
+
/**
|
|
125
|
+
* Processes items in the queuer
|
|
126
|
+
*/
|
|
127
|
+
private tick;
|
|
128
|
+
/**
|
|
129
|
+
* Stops the queuer from processing items
|
|
130
|
+
*/
|
|
131
|
+
stop(): void;
|
|
132
|
+
/**
|
|
133
|
+
* Starts the queuer and processes items
|
|
134
|
+
*/
|
|
135
|
+
start(): void;
|
|
136
|
+
/**
|
|
137
|
+
* Removes all items from the queuer
|
|
138
|
+
*/
|
|
139
|
+
clear(): void;
|
|
140
|
+
/**
|
|
141
|
+
* Resets the queuer to its initial state
|
|
142
|
+
*/
|
|
143
|
+
reset(withInitialItems?: boolean): void;
|
|
115
144
|
/**
|
|
116
145
|
* Adds an item to the queuer and starts processing if not already running
|
|
117
146
|
* @returns true if item was added, false if queuer is full
|
|
@@ -135,32 +164,24 @@ export declare class Queuer<TValue> {
|
|
|
135
164
|
* @example
|
|
136
165
|
* ```ts
|
|
137
166
|
* // Look at next item to getNextItem
|
|
138
|
-
* queuer.
|
|
167
|
+
* queuer.getPeek()
|
|
139
168
|
* // Look at last item (like stack top)
|
|
140
|
-
* queuer.
|
|
169
|
+
* queuer.getPeek('back')
|
|
141
170
|
* ```
|
|
142
171
|
*/
|
|
143
|
-
|
|
172
|
+
getPeek(position?: QueuePosition): TValue | undefined;
|
|
144
173
|
/**
|
|
145
174
|
* Returns true if the queuer is empty
|
|
146
175
|
*/
|
|
147
|
-
|
|
176
|
+
getIsEmpty(): boolean;
|
|
148
177
|
/**
|
|
149
178
|
* Returns true if the queuer is full
|
|
150
179
|
*/
|
|
151
|
-
|
|
180
|
+
getIsFull(): boolean;
|
|
152
181
|
/**
|
|
153
182
|
* Returns the current size of the queuer
|
|
154
183
|
*/
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Removes all items from the queuer
|
|
158
|
-
*/
|
|
159
|
-
clear(): void;
|
|
160
|
-
/**
|
|
161
|
-
* Resets the queuer to its initial state
|
|
162
|
-
*/
|
|
163
|
-
reset(withInitialItems?: boolean): void;
|
|
184
|
+
getSize(): number;
|
|
164
185
|
/**
|
|
165
186
|
* Returns a copy of all items in the queuer
|
|
166
187
|
*/
|
|
@@ -170,25 +191,17 @@ export declare class Queuer<TValue> {
|
|
|
170
191
|
*/
|
|
171
192
|
getExecutionCount(): number;
|
|
172
193
|
/**
|
|
173
|
-
*
|
|
174
|
-
*/
|
|
175
|
-
onUpdate(cb: (item: TValue) => void): () => void;
|
|
176
|
-
/**
|
|
177
|
-
* Stops the queuer from processing items
|
|
194
|
+
* Returns the number of items that have been rejected from the queuer
|
|
178
195
|
*/
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Starts the queuer and processes items
|
|
182
|
-
*/
|
|
183
|
-
start(): void;
|
|
196
|
+
getRejectionCount(): number;
|
|
184
197
|
/**
|
|
185
198
|
* Returns true if the queuer is running
|
|
186
199
|
*/
|
|
187
|
-
|
|
200
|
+
getIsRunning(): boolean;
|
|
188
201
|
/**
|
|
189
202
|
* Returns true if the queuer is running but has no items to process
|
|
190
203
|
*/
|
|
191
|
-
|
|
204
|
+
getIsIdle(): boolean;
|
|
192
205
|
}
|
|
193
206
|
/**
|
|
194
207
|
* Creates a queue that processes items in a queuer immediately upon addition.
|
|
@@ -204,7 +217,7 @@ export declare class Queuer<TValue> {
|
|
|
204
217
|
* // Basic sequential processing
|
|
205
218
|
* const processItems = queuer<number>({
|
|
206
219
|
* wait: 1000,
|
|
207
|
-
*
|
|
220
|
+
* onItemsChange: (queuer) => console.log(queuer.getAllItems())
|
|
208
221
|
* })
|
|
209
222
|
* processItems(1) // Logs: 1
|
|
210
223
|
* processItems(2) // Logs: 2 after 1 completes
|
package/dist/esm/queuer.js
CHANGED
|
@@ -1,91 +1,141 @@
|
|
|
1
1
|
const defaultOptions = {
|
|
2
2
|
addItemsTo: "back",
|
|
3
3
|
getItemsFrom: "front",
|
|
4
|
-
getPriority: () => 0,
|
|
4
|
+
getPriority: (item) => (item == null ? void 0 : item.priority) ?? 0,
|
|
5
5
|
initialItems: [],
|
|
6
6
|
maxSize: Infinity,
|
|
7
7
|
onGetNextItem: () => {
|
|
8
8
|
},
|
|
9
|
-
|
|
9
|
+
onIsRunningChange: () => {
|
|
10
|
+
},
|
|
11
|
+
onItemsChange: () => {
|
|
12
|
+
},
|
|
13
|
+
onReject: () => {
|
|
10
14
|
},
|
|
11
15
|
started: false,
|
|
12
16
|
wait: 0
|
|
13
17
|
};
|
|
14
18
|
class Queuer {
|
|
15
19
|
constructor(initialOptions = defaultOptions) {
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
20
|
+
this._items = [];
|
|
21
|
+
this._executionCount = 0;
|
|
22
|
+
this._rejectionCount = 0;
|
|
23
|
+
this._onItemsChanges = [];
|
|
24
|
+
this._pendingTick = false;
|
|
25
|
+
this._options = { ...defaultOptions, ...initialOptions };
|
|
26
|
+
this._running = this._options.started;
|
|
27
|
+
for (let i = 0; i < this._options.initialItems.length; i++) {
|
|
28
|
+
const item = this._options.initialItems[i];
|
|
29
|
+
const isLast = i === this._options.initialItems.length - 1;
|
|
30
|
+
this.addItem(item, this._options.addItemsTo, isLast);
|
|
26
31
|
}
|
|
27
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Updates the queuer options
|
|
35
|
+
* Returns the new options state
|
|
36
|
+
*/
|
|
37
|
+
setOptions(newOptions) {
|
|
38
|
+
this._options = { ...this._options, ...newOptions };
|
|
39
|
+
return this._options;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns the current queuer options
|
|
43
|
+
*/
|
|
44
|
+
getOptions() {
|
|
45
|
+
return this._options;
|
|
46
|
+
}
|
|
28
47
|
/**
|
|
29
48
|
* Processes items in the queuer
|
|
30
49
|
*/
|
|
31
50
|
tick() {
|
|
32
|
-
if (!this.
|
|
33
|
-
this.
|
|
51
|
+
if (!this._running) {
|
|
52
|
+
this._pendingTick = false;
|
|
34
53
|
return;
|
|
35
54
|
}
|
|
36
|
-
while (!this.
|
|
37
|
-
const nextItem = this.getNextItem(this.
|
|
55
|
+
while (!this.getIsEmpty()) {
|
|
56
|
+
const nextItem = this.getNextItem(this._options.getItemsFrom);
|
|
38
57
|
if (nextItem === void 0) {
|
|
39
58
|
break;
|
|
40
59
|
}
|
|
41
|
-
this.
|
|
42
|
-
if (this.
|
|
43
|
-
setTimeout(() => this.tick(), this.
|
|
60
|
+
this._onItemsChanges.forEach((cb) => cb(nextItem));
|
|
61
|
+
if (this._options.wait > 0) {
|
|
62
|
+
setTimeout(() => this.tick(), this._options.wait);
|
|
44
63
|
return;
|
|
45
64
|
}
|
|
46
65
|
this.tick();
|
|
47
66
|
}
|
|
48
|
-
this.
|
|
67
|
+
this._pendingTick = false;
|
|
49
68
|
}
|
|
50
69
|
/**
|
|
51
|
-
*
|
|
52
|
-
* Returns the new options state
|
|
70
|
+
* Stops the queuer from processing items
|
|
53
71
|
*/
|
|
54
|
-
|
|
55
|
-
this.
|
|
56
|
-
|
|
72
|
+
stop() {
|
|
73
|
+
this._running = false;
|
|
74
|
+
this._pendingTick = false;
|
|
75
|
+
this._options.onIsRunningChange(this);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Starts the queuer and processes items
|
|
79
|
+
*/
|
|
80
|
+
start() {
|
|
81
|
+
this._running = true;
|
|
82
|
+
if (!this._pendingTick && !this.getIsEmpty()) {
|
|
83
|
+
this._pendingTick = true;
|
|
84
|
+
this.tick();
|
|
85
|
+
}
|
|
86
|
+
this._options.onIsRunningChange(this);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Removes all items from the queuer
|
|
90
|
+
*/
|
|
91
|
+
clear() {
|
|
92
|
+
this._items = [];
|
|
93
|
+
this._options.onItemsChange(this);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Resets the queuer to its initial state
|
|
97
|
+
*/
|
|
98
|
+
reset(withInitialItems) {
|
|
99
|
+
this.clear();
|
|
100
|
+
this._executionCount = 0;
|
|
101
|
+
if (withInitialItems) {
|
|
102
|
+
this._items = [...this._options.initialItems];
|
|
103
|
+
}
|
|
104
|
+
this._running = this._options.started;
|
|
57
105
|
}
|
|
58
106
|
/**
|
|
59
107
|
* Adds an item to the queuer and starts processing if not already running
|
|
60
108
|
* @returns true if item was added, false if queuer is full
|
|
61
109
|
*/
|
|
62
|
-
addItem(item, position = this.
|
|
63
|
-
if (this.
|
|
110
|
+
addItem(item, position = this._options.addItemsTo, runOnUpdate = true) {
|
|
111
|
+
if (this.getIsFull()) {
|
|
112
|
+
this._rejectionCount++;
|
|
113
|
+
this._options.onReject(item, this);
|
|
64
114
|
return false;
|
|
65
115
|
}
|
|
66
|
-
if (this.
|
|
67
|
-
const priority = this.
|
|
68
|
-
const insertIndex = this.
|
|
69
|
-
(existing) => this.
|
|
116
|
+
if (this._options.getPriority !== defaultOptions.getPriority) {
|
|
117
|
+
const priority = this._options.getPriority(item);
|
|
118
|
+
const insertIndex = this._items.findIndex(
|
|
119
|
+
(existing) => this._options.getPriority(existing) > priority
|
|
70
120
|
);
|
|
71
121
|
if (insertIndex === -1) {
|
|
72
|
-
this.
|
|
122
|
+
this._items.push(item);
|
|
73
123
|
} else {
|
|
74
|
-
this.
|
|
124
|
+
this._items.splice(insertIndex, 0, item);
|
|
75
125
|
}
|
|
76
126
|
} else {
|
|
77
127
|
if (position === "front") {
|
|
78
|
-
this.
|
|
128
|
+
this._items.unshift(item);
|
|
79
129
|
} else {
|
|
80
|
-
this.
|
|
130
|
+
this._items.push(item);
|
|
81
131
|
}
|
|
82
132
|
}
|
|
83
|
-
if (this.
|
|
84
|
-
this.
|
|
133
|
+
if (this._running && !this._pendingTick) {
|
|
134
|
+
this._pendingTick = true;
|
|
85
135
|
this.tick();
|
|
86
136
|
}
|
|
87
137
|
if (runOnUpdate) {
|
|
88
|
-
this.
|
|
138
|
+
this._options.onItemsChange(this);
|
|
89
139
|
}
|
|
90
140
|
return true;
|
|
91
141
|
}
|
|
@@ -100,17 +150,17 @@ class Queuer {
|
|
|
100
150
|
* queuer.getNextItem('back')
|
|
101
151
|
* ```
|
|
102
152
|
*/
|
|
103
|
-
getNextItem(position = this.
|
|
153
|
+
getNextItem(position = this._options.getItemsFrom) {
|
|
104
154
|
let item;
|
|
105
155
|
if (position === "front") {
|
|
106
|
-
item = this.
|
|
156
|
+
item = this._items.shift();
|
|
107
157
|
} else {
|
|
108
|
-
item = this.
|
|
158
|
+
item = this._items.pop();
|
|
109
159
|
}
|
|
110
160
|
if (item !== void 0) {
|
|
111
|
-
this.
|
|
112
|
-
this.
|
|
113
|
-
this.
|
|
161
|
+
this._executionCount++;
|
|
162
|
+
this._options.onItemsChange(this);
|
|
163
|
+
this._options.onGetNextItem(item, this);
|
|
114
164
|
}
|
|
115
165
|
return item;
|
|
116
166
|
}
|
|
@@ -120,104 +170,64 @@ class Queuer {
|
|
|
120
170
|
* @example
|
|
121
171
|
* ```ts
|
|
122
172
|
* // Look at next item to getNextItem
|
|
123
|
-
* queuer.
|
|
173
|
+
* queuer.getPeek()
|
|
124
174
|
* // Look at last item (like stack top)
|
|
125
|
-
* queuer.
|
|
175
|
+
* queuer.getPeek('back')
|
|
126
176
|
* ```
|
|
127
177
|
*/
|
|
128
|
-
|
|
178
|
+
getPeek(position = this._options.getItemsFrom) {
|
|
129
179
|
if (position === "front") {
|
|
130
|
-
return this.
|
|
180
|
+
return this._items[0];
|
|
131
181
|
}
|
|
132
|
-
return this.
|
|
182
|
+
return this._items[this._items.length - 1];
|
|
133
183
|
}
|
|
134
184
|
/**
|
|
135
185
|
* Returns true if the queuer is empty
|
|
136
186
|
*/
|
|
137
|
-
|
|
138
|
-
return this.
|
|
187
|
+
getIsEmpty() {
|
|
188
|
+
return this._items.length === 0;
|
|
139
189
|
}
|
|
140
190
|
/**
|
|
141
191
|
* Returns true if the queuer is full
|
|
142
192
|
*/
|
|
143
|
-
|
|
144
|
-
return this.
|
|
193
|
+
getIsFull() {
|
|
194
|
+
return this._items.length >= this._options.maxSize;
|
|
145
195
|
}
|
|
146
196
|
/**
|
|
147
197
|
* Returns the current size of the queuer
|
|
148
198
|
*/
|
|
149
|
-
|
|
150
|
-
return this.
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Removes all items from the queuer
|
|
154
|
-
*/
|
|
155
|
-
clear() {
|
|
156
|
-
this.items = [];
|
|
157
|
-
this.options.onUpdate(this);
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Resets the queuer to its initial state
|
|
161
|
-
*/
|
|
162
|
-
reset(withInitialItems) {
|
|
163
|
-
this.clear();
|
|
164
|
-
this.executionCount = 0;
|
|
165
|
-
if (withInitialItems) {
|
|
166
|
-
this.items = [...this.options.initialItems];
|
|
167
|
-
}
|
|
168
|
-
this.running = this.options.started;
|
|
199
|
+
getSize() {
|
|
200
|
+
return this._items.length;
|
|
169
201
|
}
|
|
170
202
|
/**
|
|
171
203
|
* Returns a copy of all items in the queuer
|
|
172
204
|
*/
|
|
173
205
|
getAllItems() {
|
|
174
|
-
return [...this.
|
|
206
|
+
return [...this._items];
|
|
175
207
|
}
|
|
176
208
|
/**
|
|
177
209
|
* Returns the number of items that have been removed from the queuer
|
|
178
210
|
*/
|
|
179
211
|
getExecutionCount() {
|
|
180
|
-
return this.
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Adds a callback to be called when an item is processed
|
|
184
|
-
*/
|
|
185
|
-
onUpdate(cb) {
|
|
186
|
-
this.onUpdates.push(cb);
|
|
187
|
-
return () => {
|
|
188
|
-
this.onUpdates = this.onUpdates.filter((d) => d !== cb);
|
|
189
|
-
};
|
|
212
|
+
return this._executionCount;
|
|
190
213
|
}
|
|
191
214
|
/**
|
|
192
|
-
*
|
|
215
|
+
* Returns the number of items that have been rejected from the queuer
|
|
193
216
|
*/
|
|
194
|
-
|
|
195
|
-
this.
|
|
196
|
-
this.pendingTick = false;
|
|
197
|
-
this.options.onUpdate(this);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Starts the queuer and processes items
|
|
201
|
-
*/
|
|
202
|
-
start() {
|
|
203
|
-
this.running = true;
|
|
204
|
-
if (!this.pendingTick && !this.isEmpty()) {
|
|
205
|
-
this.pendingTick = true;
|
|
206
|
-
this.tick();
|
|
207
|
-
}
|
|
208
|
-
this.options.onUpdate(this);
|
|
217
|
+
getRejectionCount() {
|
|
218
|
+
return this._rejectionCount;
|
|
209
219
|
}
|
|
210
220
|
/**
|
|
211
221
|
* Returns true if the queuer is running
|
|
212
222
|
*/
|
|
213
|
-
|
|
214
|
-
return this.
|
|
223
|
+
getIsRunning() {
|
|
224
|
+
return this._running;
|
|
215
225
|
}
|
|
216
226
|
/**
|
|
217
227
|
* Returns true if the queuer is running but has no items to process
|
|
218
228
|
*/
|
|
219
|
-
|
|
220
|
-
return this.
|
|
229
|
+
getIsIdle() {
|
|
230
|
+
return this._running && this.getIsEmpty();
|
|
221
231
|
}
|
|
222
232
|
}
|
|
223
233
|
function queue(options = {}) {
|