@tanstack/pacer 0.7.0 → 0.9.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-batcher.cjs +163 -0
- package/dist/cjs/async-batcher.cjs.map +1 -0
- package/dist/cjs/async-batcher.d.cts +273 -0
- package/dist/cjs/async-debouncer.cjs +149 -162
- package/dist/cjs/async-debouncer.cjs.map +1 -1
- package/dist/cjs/async-debouncer.d.cts +76 -57
- package/dist/cjs/async-queuer.cjs +282 -343
- package/dist/cjs/async-queuer.cjs.map +1 -1
- package/dist/cjs/async-queuer.d.cts +123 -102
- package/dist/cjs/async-rate-limiter.cjs +128 -185
- package/dist/cjs/async-rate-limiter.cjs.map +1 -1
- package/dist/cjs/async-rate-limiter.d.cts +72 -61
- package/dist/cjs/async-throttler.cjs +168 -178
- package/dist/cjs/async-throttler.cjs.map +1 -1
- package/dist/cjs/async-throttler.d.cts +97 -69
- package/dist/cjs/batcher.cjs +110 -119
- package/dist/cjs/batcher.cjs.map +1 -1
- package/dist/cjs/batcher.d.cts +76 -51
- package/dist/cjs/debouncer.cjs +97 -85
- package/dist/cjs/debouncer.cjs.map +1 -1
- package/dist/cjs/debouncer.d.cts +54 -26
- package/dist/cjs/index.cjs +3 -6
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/queuer.cjs +246 -294
- package/dist/cjs/queuer.cjs.map +1 -1
- package/dist/cjs/queuer.d.cts +105 -84
- package/dist/cjs/rate-limiter.cjs +97 -130
- package/dist/cjs/rate-limiter.cjs.map +1 -1
- package/dist/cjs/rate-limiter.d.cts +50 -37
- package/dist/cjs/throttler.cjs +107 -123
- package/dist/cjs/throttler.cjs.map +1 -1
- package/dist/cjs/throttler.d.cts +59 -35
- package/dist/cjs/utils.cjs +0 -13
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +0 -1
- package/dist/esm/async-batcher.d.ts +273 -0
- package/dist/esm/async-batcher.js +163 -0
- package/dist/esm/async-batcher.js.map +1 -0
- package/dist/esm/async-debouncer.d.ts +76 -57
- package/dist/esm/async-debouncer.js +149 -162
- package/dist/esm/async-debouncer.js.map +1 -1
- package/dist/esm/async-queuer.d.ts +123 -102
- package/dist/esm/async-queuer.js +282 -343
- package/dist/esm/async-queuer.js.map +1 -1
- package/dist/esm/async-rate-limiter.d.ts +72 -61
- package/dist/esm/async-rate-limiter.js +128 -185
- package/dist/esm/async-rate-limiter.js.map +1 -1
- package/dist/esm/async-throttler.d.ts +97 -69
- package/dist/esm/async-throttler.js +168 -178
- package/dist/esm/async-throttler.js.map +1 -1
- package/dist/esm/batcher.d.ts +76 -51
- package/dist/esm/batcher.js +110 -119
- package/dist/esm/batcher.js.map +1 -1
- package/dist/esm/debouncer.d.ts +54 -26
- package/dist/esm/debouncer.js +97 -85
- package/dist/esm/debouncer.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +4 -7
- package/dist/esm/queuer.d.ts +105 -84
- package/dist/esm/queuer.js +246 -294
- package/dist/esm/queuer.js.map +1 -1
- package/dist/esm/rate-limiter.d.ts +50 -37
- package/dist/esm/rate-limiter.js +97 -130
- package/dist/esm/rate-limiter.js.map +1 -1
- package/dist/esm/throttler.d.ts +59 -35
- package/dist/esm/throttler.js +107 -123
- package/dist/esm/throttler.js.map +1 -1
- package/dist/esm/utils.d.ts +0 -1
- package/dist/esm/utils.js +0 -13
- package/dist/esm/utils.js.map +1 -1
- package/package.json +14 -11
- package/src/async-batcher.ts +475 -0
- package/src/async-debouncer.ts +201 -121
- package/src/async-queuer.ts +341 -220
- package/src/async-rate-limiter.ts +176 -136
- package/src/async-throttler.ts +233 -139
- package/src/batcher.ts +159 -93
- package/src/debouncer.ts +135 -52
- package/src/index.ts +1 -1
- package/src/queuer.ts +349 -227
- package/src/rate-limiter.ts +125 -80
- package/src/throttler.ts +152 -78
- package/src/utils.ts +0 -15
- package/dist/cjs/compare.cjs +0 -72
- package/dist/cjs/compare.cjs.map +0 -1
- package/dist/cjs/compare.d.cts +0 -12
- package/dist/esm/compare.d.ts +0 -12
- package/dist/esm/compare.js +0 -72
- package/dist/esm/compare.js.map +0 -1
- package/src/compare.ts +0 -105
|
@@ -1,378 +1,317 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const store = require("@tanstack/store");
|
|
3
4
|
const utils = require("./utils.cjs");
|
|
5
|
+
function getDefaultAsyncQueuerState() {
|
|
6
|
+
return structuredClone({
|
|
7
|
+
activeItems: [],
|
|
8
|
+
errorCount: 0,
|
|
9
|
+
expirationCount: 0,
|
|
10
|
+
isEmpty: true,
|
|
11
|
+
isFull: false,
|
|
12
|
+
isIdle: true,
|
|
13
|
+
isRunning: true,
|
|
14
|
+
itemTimestamps: [],
|
|
15
|
+
items: [],
|
|
16
|
+
lastResult: null,
|
|
17
|
+
pendingTick: false,
|
|
18
|
+
rejectionCount: 0,
|
|
19
|
+
settledCount: 0,
|
|
20
|
+
size: 0,
|
|
21
|
+
status: "idle",
|
|
22
|
+
successCount: 0
|
|
23
|
+
});
|
|
24
|
+
}
|
|
4
25
|
const defaultOptions = {
|
|
5
26
|
addItemsTo: "back",
|
|
6
27
|
concurrency: 1,
|
|
7
28
|
expirationDuration: Infinity,
|
|
8
29
|
getIsExpired: () => false,
|
|
9
30
|
getItemsFrom: "front",
|
|
10
|
-
getPriority: (item) =>
|
|
31
|
+
getPriority: (item) => item?.priority ?? 0,
|
|
11
32
|
initialItems: [],
|
|
12
33
|
maxSize: Infinity,
|
|
13
34
|
started: true,
|
|
14
35
|
wait: 0
|
|
15
36
|
};
|
|
16
37
|
class AsyncQueuer {
|
|
17
|
-
constructor(fn, initialOptions) {
|
|
38
|
+
constructor(fn, initialOptions = {}) {
|
|
18
39
|
this.fn = fn;
|
|
19
|
-
this.
|
|
20
|
-
this
|
|
21
|
-
this.
|
|
22
|
-
|
|
23
|
-
this._rejectionCount = 0;
|
|
24
|
-
this._expirationCount = 0;
|
|
25
|
-
this._items = [];
|
|
26
|
-
this._itemTimestamps = [];
|
|
27
|
-
this._pendingTick = false;
|
|
28
|
-
this._options = {
|
|
29
|
-
...defaultOptions,
|
|
30
|
-
...initialOptions,
|
|
31
|
-
throwOnError: initialOptions.throwOnError ?? !initialOptions.onError
|
|
40
|
+
this.store = new store.Store(getDefaultAsyncQueuerState());
|
|
41
|
+
this.#timeoutIds = /* @__PURE__ */ new Set();
|
|
42
|
+
this.setOptions = (newOptions) => {
|
|
43
|
+
this.options = { ...this.options, ...newOptions };
|
|
32
44
|
};
|
|
33
|
-
this
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Processes items in the queue up to the concurrency limit. Internal use only.
|
|
68
|
-
*/
|
|
69
|
-
tick() {
|
|
70
|
-
var _a, _b;
|
|
71
|
-
if (!this._running) {
|
|
72
|
-
this._pendingTick = false;
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
this.checkExpiredItems();
|
|
76
|
-
while (this._activeItems.size < this.getConcurrency() && !this.getIsEmpty()) {
|
|
77
|
-
const nextItem = this.getPeek();
|
|
78
|
-
if (!nextItem) {
|
|
79
|
-
break;
|
|
45
|
+
this.#setState = (newState) => {
|
|
46
|
+
this.store.setState((state) => {
|
|
47
|
+
const combinedState = {
|
|
48
|
+
...state,
|
|
49
|
+
...newState
|
|
50
|
+
};
|
|
51
|
+
const { activeItems, items, isRunning } = combinedState;
|
|
52
|
+
const size = items.length;
|
|
53
|
+
const isFull = size >= (this.options.maxSize ?? Infinity);
|
|
54
|
+
const isEmpty = size === 0;
|
|
55
|
+
const isIdle = isRunning && isEmpty && activeItems.length === 0;
|
|
56
|
+
const status = isIdle ? "idle" : isRunning ? "running" : "stopped";
|
|
57
|
+
return {
|
|
58
|
+
...combinedState,
|
|
59
|
+
isEmpty,
|
|
60
|
+
isFull,
|
|
61
|
+
isIdle,
|
|
62
|
+
size,
|
|
63
|
+
status
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
this.#getWait = () => {
|
|
68
|
+
return utils.parseFunctionOrValue(this.options.wait ?? 0, this);
|
|
69
|
+
};
|
|
70
|
+
this.#getConcurrency = () => {
|
|
71
|
+
return utils.parseFunctionOrValue(this.options.concurrency ?? 1, this);
|
|
72
|
+
};
|
|
73
|
+
this.#tick = () => {
|
|
74
|
+
if (!this.store.state.isRunning) {
|
|
75
|
+
this.#setState({ pendingTick: false });
|
|
76
|
+
return;
|
|
80
77
|
}
|
|
81
|
-
this
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
this.#setState({ pendingTick: true });
|
|
79
|
+
this.#checkExpiredItems();
|
|
80
|
+
const activeItems = this.store.state.activeItems;
|
|
81
|
+
while (activeItems.length < this.#getConcurrency() && !this.store.state.isEmpty) {
|
|
82
|
+
const nextItem = this.peekNextItem();
|
|
83
|
+
if (!nextItem) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
activeItems.push(nextItem);
|
|
87
|
+
this.#setState({
|
|
88
|
+
activeItems
|
|
89
|
+
});
|
|
90
|
+
(async () => {
|
|
91
|
+
const result = await this.execute();
|
|
92
|
+
this.#setState({ lastResult: result });
|
|
93
|
+
const wait = this.#getWait();
|
|
94
|
+
if (wait > 0) {
|
|
95
|
+
const timeoutId = setTimeout(() => this.#tick(), wait);
|
|
96
|
+
this.#timeoutIds.add(timeoutId);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.#tick();
|
|
100
|
+
})();
|
|
101
|
+
}
|
|
102
|
+
this.#setState({ pendingTick: false });
|
|
103
|
+
};
|
|
104
|
+
this.addItem = (item, position = this.options.addItemsTo ?? "back", runOnItemsChange = true) => {
|
|
105
|
+
if (this.store.state.isFull) {
|
|
106
|
+
this.#setState({
|
|
107
|
+
rejectionCount: this.store.state.rejectionCount + 1
|
|
108
|
+
});
|
|
109
|
+
this.options.onReject?.(item, this);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
const priority = this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(item) : item.priority;
|
|
113
|
+
const items = this.store.state.items;
|
|
114
|
+
const itemTimestamps = this.store.state.itemTimestamps;
|
|
115
|
+
if (priority !== void 0) {
|
|
116
|
+
const insertIndex = items.findIndex((existing) => {
|
|
117
|
+
const existingPriority = this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(existing) : existing.priority;
|
|
118
|
+
return existingPriority < priority;
|
|
119
|
+
});
|
|
120
|
+
if (insertIndex === -1) {
|
|
121
|
+
items.push(item);
|
|
122
|
+
itemTimestamps.push(Date.now());
|
|
123
|
+
} else {
|
|
124
|
+
items.splice(insertIndex, 0, item);
|
|
125
|
+
itemTimestamps.splice(insertIndex, 0, Date.now());
|
|
89
126
|
}
|
|
90
|
-
this.tick();
|
|
91
|
-
})();
|
|
92
|
-
}
|
|
93
|
-
this._pendingTick = false;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Starts processing items in the queue. If already running, does nothing.
|
|
97
|
-
*/
|
|
98
|
-
start() {
|
|
99
|
-
var _a, _b;
|
|
100
|
-
this._running = true;
|
|
101
|
-
if (!this._pendingTick && !this.getIsEmpty()) {
|
|
102
|
-
this._pendingTick = true;
|
|
103
|
-
this.tick();
|
|
104
|
-
}
|
|
105
|
-
(_b = (_a = this._options).onIsRunningChange) == null ? void 0 : _b.call(_a, this);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Stops processing items in the queue. Does not clear the queue.
|
|
109
|
-
*/
|
|
110
|
-
stop() {
|
|
111
|
-
var _a, _b;
|
|
112
|
-
this._running = false;
|
|
113
|
-
this._pendingTick = false;
|
|
114
|
-
(_b = (_a = this._options).onIsRunningChange) == null ? void 0 : _b.call(_a, this);
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Removes all pending items from the queue. Does not affect active tasks.
|
|
118
|
-
*/
|
|
119
|
-
clear() {
|
|
120
|
-
var _a, _b;
|
|
121
|
-
this._items = [];
|
|
122
|
-
(_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Resets the queuer to its initial state. Optionally repopulates with initial items.
|
|
126
|
-
* Does not affect callbacks or options.
|
|
127
|
-
*/
|
|
128
|
-
reset(withInitialItems) {
|
|
129
|
-
this.clear();
|
|
130
|
-
this._successCount = 0;
|
|
131
|
-
this._errorCount = 0;
|
|
132
|
-
this._settledCount = 0;
|
|
133
|
-
if (withInitialItems) {
|
|
134
|
-
this._items = [...this._options.initialItems];
|
|
135
|
-
}
|
|
136
|
-
this._running = this._options.started;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.
|
|
140
|
-
* Items can be inserted based on priority or at the front/back depending on configuration.
|
|
141
|
-
*
|
|
142
|
-
* @example
|
|
143
|
-
* ```ts
|
|
144
|
-
* queuer.addItem({ value: 'task', priority: 10 });
|
|
145
|
-
* queuer.addItem('task2', 'front');
|
|
146
|
-
* ```
|
|
147
|
-
*/
|
|
148
|
-
addItem(item, position = this._options.addItemsTo, runOnItemsChange = true) {
|
|
149
|
-
var _a, _b, _c, _d;
|
|
150
|
-
if (this.getIsFull()) {
|
|
151
|
-
this._rejectionCount++;
|
|
152
|
-
(_b = (_a = this._options).onReject) == null ? void 0 : _b.call(_a, item, this);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const priority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(item) : item.priority;
|
|
156
|
-
if (priority !== void 0) {
|
|
157
|
-
const insertIndex = this._items.findIndex((existing) => {
|
|
158
|
-
const existingPriority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(existing) : existing.priority;
|
|
159
|
-
return existingPriority < priority;
|
|
160
|
-
});
|
|
161
|
-
if (insertIndex === -1) {
|
|
162
|
-
this._items.push(item);
|
|
163
|
-
this._itemTimestamps.push(Date.now());
|
|
164
127
|
} else {
|
|
165
|
-
|
|
166
|
-
|
|
128
|
+
if (position === "front") {
|
|
129
|
+
items.unshift(item);
|
|
130
|
+
itemTimestamps.unshift(Date.now());
|
|
131
|
+
} else {
|
|
132
|
+
items.push(item);
|
|
133
|
+
itemTimestamps.push(Date.now());
|
|
134
|
+
}
|
|
167
135
|
}
|
|
168
|
-
|
|
136
|
+
this.#setState({
|
|
137
|
+
items,
|
|
138
|
+
itemTimestamps
|
|
139
|
+
});
|
|
140
|
+
if (runOnItemsChange) {
|
|
141
|
+
this.options.onItemsChange?.(this);
|
|
142
|
+
}
|
|
143
|
+
if (this.store.state.isRunning && !this.store.state.pendingTick) {
|
|
144
|
+
this.#tick();
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
};
|
|
148
|
+
this.getNextItem = (position = this.options.getItemsFrom ?? "front") => {
|
|
149
|
+
const { items, itemTimestamps } = this.store.state;
|
|
150
|
+
let item;
|
|
169
151
|
if (position === "front") {
|
|
170
|
-
|
|
171
|
-
|
|
152
|
+
item = items[0];
|
|
153
|
+
if (item !== void 0) {
|
|
154
|
+
this.#setState({
|
|
155
|
+
items: items.slice(1),
|
|
156
|
+
itemTimestamps: itemTimestamps.slice(1)
|
|
157
|
+
});
|
|
158
|
+
}
|
|
172
159
|
} else {
|
|
173
|
-
|
|
174
|
-
|
|
160
|
+
item = items[items.length - 1];
|
|
161
|
+
if (item !== void 0) {
|
|
162
|
+
this.#setState({
|
|
163
|
+
items: items.slice(0, -1),
|
|
164
|
+
itemTimestamps: itemTimestamps.slice(0, -1)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
175
167
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
this.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (item !== void 0) {
|
|
208
|
-
(_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
|
|
209
|
-
}
|
|
210
|
-
return item;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Removes and returns the next item from the queue and executes the task function with it.
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* ```ts
|
|
217
|
-
* queuer.execute();
|
|
218
|
-
* // LIFO
|
|
219
|
-
* queuer.execute('back');
|
|
220
|
-
* ```
|
|
221
|
-
*/
|
|
222
|
-
async execute(position) {
|
|
223
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
224
|
-
const item = this.getNextItem(position);
|
|
225
|
-
if (item !== void 0) {
|
|
226
|
-
try {
|
|
227
|
-
this._lastResult = await this.fn(item);
|
|
228
|
-
this._successCount++;
|
|
229
|
-
(_b = (_a = this._options).onSuccess) == null ? void 0 : _b.call(_a, this._lastResult, this);
|
|
230
|
-
} catch (error) {
|
|
231
|
-
this._errorCount++;
|
|
232
|
-
(_d = (_c = this._options).onError) == null ? void 0 : _d.call(_c, error, this);
|
|
233
|
-
if (this._options.throwOnError) {
|
|
234
|
-
throw error;
|
|
168
|
+
if (item !== void 0) {
|
|
169
|
+
this.options.onItemsChange?.(this);
|
|
170
|
+
}
|
|
171
|
+
return item;
|
|
172
|
+
};
|
|
173
|
+
this.execute = async (position) => {
|
|
174
|
+
const item = this.getNextItem(position);
|
|
175
|
+
if (item !== void 0) {
|
|
176
|
+
try {
|
|
177
|
+
const lastResult = await this.fn(item);
|
|
178
|
+
this.#setState({
|
|
179
|
+
successCount: this.store.state.successCount + 1,
|
|
180
|
+
lastResult
|
|
181
|
+
});
|
|
182
|
+
this.options.onSuccess?.(lastResult, this);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.#setState({
|
|
185
|
+
errorCount: this.store.state.errorCount + 1
|
|
186
|
+
});
|
|
187
|
+
this.options.onError?.(error, this);
|
|
188
|
+
if (this.options.throwOnError) {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
} finally {
|
|
192
|
+
this.#setState({
|
|
193
|
+
activeItems: this.store.state.activeItems.filter(
|
|
194
|
+
(activeItem) => activeItem !== item
|
|
195
|
+
),
|
|
196
|
+
settledCount: this.store.state.settledCount + 1
|
|
197
|
+
});
|
|
198
|
+
this.options.onSettled?.(this);
|
|
235
199
|
}
|
|
236
|
-
} finally {
|
|
237
|
-
this._settledCount++;
|
|
238
|
-
this._activeItems.delete(item);
|
|
239
|
-
(_f = (_e = this._options).onItemsChange) == null ? void 0 : _f.call(_e, this);
|
|
240
|
-
(_h = (_g = this._options).onSettled) == null ? void 0 : _h.call(_g, this);
|
|
241
200
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
201
|
+
return item;
|
|
202
|
+
};
|
|
203
|
+
this.flush = (numberOfItems = this.store.state.items.length, position) => {
|
|
204
|
+
this.#clearTimeouts();
|
|
205
|
+
for (let i = 0; i < numberOfItems; i++) {
|
|
206
|
+
this.execute(position);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
this.#checkExpiredItems = () => {
|
|
210
|
+
if ((this.options.expirationDuration ?? Infinity) === Infinity && this.options.getIsExpired === defaultOptions.getIsExpired) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
const expiredIndices = [];
|
|
215
|
+
for (let i = 0; i < this.store.state.size; i++) {
|
|
216
|
+
const timestamp = this.store.state.itemTimestamps[i];
|
|
217
|
+
if (timestamp === void 0) continue;
|
|
218
|
+
const item = this.store.state.items[i];
|
|
219
|
+
if (item === void 0) continue;
|
|
220
|
+
const isExpired = this.options.getIsExpired !== defaultOptions.getIsExpired ? this.options.getIsExpired(item, timestamp) : now - timestamp > (this.options.expirationDuration ?? Infinity);
|
|
221
|
+
if (isExpired) {
|
|
222
|
+
expiredIndices.push(i);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for (let i = expiredIndices.length - 1; i >= 0; i--) {
|
|
226
|
+
const index = expiredIndices[i];
|
|
227
|
+
if (index === void 0) continue;
|
|
228
|
+
const expiredItem = this.store.state.items[index];
|
|
229
|
+
if (expiredItem === void 0) continue;
|
|
230
|
+
const newItems = [...this.store.state.items];
|
|
231
|
+
const newTimestamps = [...this.store.state.itemTimestamps];
|
|
232
|
+
newItems.splice(index, 1);
|
|
233
|
+
newTimestamps.splice(index, 1);
|
|
234
|
+
this.#setState({
|
|
235
|
+
items: newItems,
|
|
236
|
+
itemTimestamps: newTimestamps,
|
|
237
|
+
expirationCount: this.store.state.expirationCount + 1
|
|
238
|
+
});
|
|
239
|
+
this.options.onExpire?.(expiredItem, this);
|
|
240
|
+
}
|
|
241
|
+
if (expiredIndices.length > 0) {
|
|
242
|
+
this.options.onItemsChange?.(this);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
this.peekNextItem = (position = "front") => {
|
|
246
|
+
if (position === "front") {
|
|
247
|
+
return this.store.state.items[0];
|
|
248
|
+
}
|
|
249
|
+
return this.store.state.items[this.store.state.size - 1];
|
|
250
|
+
};
|
|
251
|
+
this.peekAllItems = () => {
|
|
252
|
+
return [...this.peekActiveItems(), ...this.peekPendingItems()];
|
|
253
|
+
};
|
|
254
|
+
this.peekActiveItems = () => {
|
|
255
|
+
return [...this.store.state.activeItems];
|
|
256
|
+
};
|
|
257
|
+
this.peekPendingItems = () => {
|
|
258
|
+
return [...this.store.state.items];
|
|
259
|
+
};
|
|
260
|
+
this.start = () => {
|
|
261
|
+
this.#setState({ isRunning: true });
|
|
262
|
+
if (!this.store.state.pendingTick && !this.store.state.isEmpty) {
|
|
263
|
+
this.#tick();
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
this.stop = () => {
|
|
267
|
+
this.#clearTimeouts();
|
|
268
|
+
this.#setState({ isRunning: false, pendingTick: false });
|
|
269
|
+
};
|
|
270
|
+
this.#clearTimeouts = () => {
|
|
271
|
+
this.#timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
|
|
272
|
+
this.#timeoutIds.clear();
|
|
273
|
+
};
|
|
274
|
+
this.clear = () => {
|
|
275
|
+
this.#setState({ items: [], itemTimestamps: [] });
|
|
276
|
+
this.options.onItemsChange?.(this);
|
|
277
|
+
};
|
|
278
|
+
this.reset = () => {
|
|
279
|
+
this.#setState(getDefaultAsyncQueuerState());
|
|
280
|
+
this.options.onItemsChange?.(this);
|
|
281
|
+
};
|
|
282
|
+
this.options = {
|
|
283
|
+
...defaultOptions,
|
|
284
|
+
...initialOptions,
|
|
285
|
+
throwOnError: initialOptions.throwOnError ?? !initialOptions.onError
|
|
286
|
+
};
|
|
287
|
+
const isInitiallyRunning = this.options.initialState?.isRunning ?? this.options.started ?? true;
|
|
288
|
+
this.#setState({
|
|
289
|
+
...this.options.initialState,
|
|
290
|
+
isRunning: isInitiallyRunning
|
|
291
|
+
});
|
|
292
|
+
if (this.options.initialState?.items) {
|
|
293
|
+
if (this.store.state.isRunning) {
|
|
294
|
+
this.#tick();
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
for (let i = 0; i < (this.options.initialItems?.length ?? 0); i++) {
|
|
298
|
+
const item = this.options.initialItems[i];
|
|
299
|
+
const isLast = i === (this.options.initialItems?.length ?? 0) - 1;
|
|
300
|
+
this.addItem(item, this.options.addItemsTo ?? "back", isLast);
|
|
263
301
|
}
|
|
264
302
|
}
|
|
265
|
-
for (let i = expiredIndices.length - 1; i >= 0; i--) {
|
|
266
|
-
const index = expiredIndices[i];
|
|
267
|
-
if (index === void 0) continue;
|
|
268
|
-
const expiredItem = this._items[index];
|
|
269
|
-
if (expiredItem === void 0) continue;
|
|
270
|
-
this._items.splice(index, 1);
|
|
271
|
-
this._itemTimestamps.splice(index, 1);
|
|
272
|
-
this._expirationCount++;
|
|
273
|
-
(_b = (_a = this._options).onExpire) == null ? void 0 : _b.call(_a, expiredItem, this);
|
|
274
|
-
}
|
|
275
|
-
if (expiredIndices.length > 0) {
|
|
276
|
-
(_d = (_c = this._options).onItemsChange) == null ? void 0 : _d.call(_c, this);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Returns the next item in the queue without removing it.
|
|
281
|
-
*
|
|
282
|
-
* @example
|
|
283
|
-
* ```ts
|
|
284
|
-
* queuer.getPeek(); // front
|
|
285
|
-
* queuer.getPeek('back'); // back
|
|
286
|
-
* ```
|
|
287
|
-
*/
|
|
288
|
-
getPeek(position = "front") {
|
|
289
|
-
if (position === "front") {
|
|
290
|
-
return this._items[0];
|
|
291
|
-
}
|
|
292
|
-
return this._items[this._items.length - 1];
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Returns true if the queue is empty (no pending items).
|
|
296
|
-
*/
|
|
297
|
-
getIsEmpty() {
|
|
298
|
-
return this._items.length === 0;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Returns true if the queue is full (reached maxSize).
|
|
302
|
-
*/
|
|
303
|
-
getIsFull() {
|
|
304
|
-
return this._items.length >= this._options.maxSize;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Returns the number of pending items in the queue.
|
|
308
|
-
*/
|
|
309
|
-
getSize() {
|
|
310
|
-
return this._items.length;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Returns a copy of all items in the queue, including active and pending items.
|
|
314
|
-
*/
|
|
315
|
-
getAllItems() {
|
|
316
|
-
return [...this.getActiveItems(), ...this.getPendingItems()];
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Returns the items currently being processed (active tasks).
|
|
320
|
-
*/
|
|
321
|
-
getActiveItems() {
|
|
322
|
-
return Array.from(this._activeItems);
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Returns the items waiting to be processed (pending tasks).
|
|
326
|
-
*/
|
|
327
|
-
getPendingItems() {
|
|
328
|
-
return [...this._items];
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Returns the number of items that have been successfully processed.
|
|
332
|
-
*/
|
|
333
|
-
getSuccessCount() {
|
|
334
|
-
return this._successCount;
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Returns the number of items that have failed processing.
|
|
338
|
-
*/
|
|
339
|
-
getErrorCount() {
|
|
340
|
-
return this._errorCount;
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Returns the number of items that have completed processing (success or error).
|
|
344
|
-
*/
|
|
345
|
-
getSettledCount() {
|
|
346
|
-
return this._settledCount;
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Returns the number of items that have been rejected from being added to the queue.
|
|
350
|
-
*/
|
|
351
|
-
getRejectionCount() {
|
|
352
|
-
return this._rejectionCount;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Returns true if the queuer is currently running (processing items).
|
|
356
|
-
*/
|
|
357
|
-
getIsRunning() {
|
|
358
|
-
return this._running;
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Returns true if the queuer is running but has no items to process and no active tasks.
|
|
362
|
-
*/
|
|
363
|
-
getIsIdle() {
|
|
364
|
-
return this._running && this.getIsEmpty() && this._activeItems.size === 0;
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Returns the number of items that have expired and been removed from the queue.
|
|
368
|
-
*/
|
|
369
|
-
getExpirationCount() {
|
|
370
|
-
return this._expirationCount;
|
|
371
303
|
}
|
|
304
|
+
#timeoutIds;
|
|
305
|
+
#setState;
|
|
306
|
+
#getWait;
|
|
307
|
+
#getConcurrency;
|
|
308
|
+
#tick;
|
|
309
|
+
#checkExpiredItems;
|
|
310
|
+
#clearTimeouts;
|
|
372
311
|
}
|
|
373
312
|
function asyncQueue(fn, initialOptions) {
|
|
374
313
|
const asyncQueuer = new AsyncQueuer(fn, initialOptions);
|
|
375
|
-
return asyncQueuer.addItem
|
|
314
|
+
return asyncQueuer.addItem;
|
|
376
315
|
}
|
|
377
316
|
exports.AsyncQueuer = AsyncQueuer;
|
|
378
317
|
exports.asyncQueue = asyncQueue;
|