@tanstack/pacer 0.22.0 → 0.23.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/README.md +4 -4
- package/dist/async-batcher.d.ts +11 -13
- package/dist/async-batcher.js +185 -131
- package/dist/async-debouncer.d.ts +6 -8
- package/dist/async-debouncer.js +186 -142
- package/dist/async-queuer.d.ts +11 -13
- package/dist/async-queuer.js +383 -290
- package/dist/async-rate-limiter.d.ts +6 -8
- package/dist/async-rate-limiter.js +197 -143
- package/dist/async-retryer.d.ts +6 -8
- package/dist/async-retryer.js +209 -179
- package/dist/async-throttler.d.ts +6 -8
- package/dist/async-throttler.js +214 -157
- package/dist/batcher.d.ts +5 -7
- package/dist/batcher.js +107 -87
- package/dist/debouncer.d.ts +6 -8
- package/dist/debouncer.js +99 -86
- package/dist/event-client.d.ts +8 -10
- package/dist/event-client.js +1 -2
- package/dist/queuer.d.ts +7 -9
- package/dist/queuer.js +277 -212
- package/dist/rate-limiter.d.ts +6 -8
- package/dist/rate-limiter.js +127 -109
- package/dist/throttler.d.ts +6 -8
- package/dist/throttler.js +127 -90
- package/dist/types.d.ts +4 -6
- package/dist/utils.d.ts +3 -5
- package/dist/utils.js +1 -2
- package/package.json +21 -68
- package/dist/async-batcher.cjs +0 -337
- package/dist/async-batcher.cjs.map +0 -1
- package/dist/async-batcher.d.cts +0 -343
- package/dist/async-batcher.js.map +0 -1
- package/dist/async-debouncer.cjs +0 -327
- package/dist/async-debouncer.cjs.map +0 -1
- package/dist/async-debouncer.d.cts +0 -299
- package/dist/async-debouncer.js.map +0 -1
- package/dist/async-queuer.cjs +0 -516
- package/dist/async-queuer.cjs.map +0 -1
- package/dist/async-queuer.d.cts +0 -440
- package/dist/async-queuer.js.map +0 -1
- package/dist/async-rate-limiter.cjs +0 -370
- package/dist/async-rate-limiter.cjs.map +0 -1
- package/dist/async-rate-limiter.d.cts +0 -356
- package/dist/async-rate-limiter.js.map +0 -1
- package/dist/async-retryer.cjs +0 -365
- package/dist/async-retryer.cjs.map +0 -1
- package/dist/async-retryer.d.cts +0 -321
- package/dist/async-retryer.js.map +0 -1
- package/dist/async-throttler.cjs +0 -344
- package/dist/async-throttler.cjs.map +0 -1
- package/dist/async-throttler.d.cts +0 -319
- package/dist/async-throttler.js.map +0 -1
- package/dist/batcher.cjs +0 -200
- package/dist/batcher.cjs.map +0 -1
- package/dist/batcher.d.cts +0 -179
- package/dist/batcher.js.map +0 -1
- package/dist/debouncer.cjs +0 -203
- package/dist/debouncer.cjs.map +0 -1
- package/dist/debouncer.d.cts +0 -166
- package/dist/debouncer.js.map +0 -1
- package/dist/event-client.cjs +0 -64
- package/dist/event-client.cjs.map +0 -1
- package/dist/event-client.d.cts +0 -65
- package/dist/event-client.js.map +0 -1
- package/dist/index.cjs +0 -52
- package/dist/index.d.cts +0 -15
- package/dist/queuer.cjs +0 -406
- package/dist/queuer.cjs.map +0 -1
- package/dist/queuer.d.cts +0 -345
- package/dist/queuer.js.map +0 -1
- package/dist/rate-limiter.cjs +0 -263
- package/dist/rate-limiter.cjs.map +0 -1
- package/dist/rate-limiter.d.cts +0 -214
- package/dist/rate-limiter.js.map +0 -1
- package/dist/throttler.cjs +0 -215
- package/dist/throttler.cjs.map +0 -1
- package/dist/throttler.d.cts +0 -206
- package/dist/throttler.js.map +0 -1
- package/dist/types.cjs +0 -0
- package/dist/types.d.cts +0 -13
- package/dist/utils.cjs +0 -13
- package/dist/utils.cjs.map +0 -1
- package/dist/utils.d.cts +0 -7
- package/dist/utils.js.map +0 -1
- package/src/async-batcher.ts +0 -594
- package/src/async-debouncer.ts +0 -566
- package/src/async-queuer.ts +0 -988
- package/src/async-rate-limiter.ts +0 -648
- package/src/async-retryer.ts +0 -673
- package/src/async-throttler.ts +0 -634
- package/src/batcher.ts +0 -329
- package/src/debouncer.ts +0 -334
- package/src/event-client.ts +0 -129
- package/src/index.ts +0 -24
- package/src/queuer.ts +0 -751
- package/src/rate-limiter.ts +0 -429
- package/src/throttler.ts +0 -380
- package/src/types.ts +0 -12
- package/src/utils.ts +0 -12
package/dist/async-retryer.js
CHANGED
|
@@ -130,7 +130,11 @@ const defaultOptions = {
|
|
|
130
130
|
* @template TFn The async function type to be retried.
|
|
131
131
|
*/
|
|
132
132
|
var AsyncRetryer = class {
|
|
133
|
-
|
|
133
|
+
fn;
|
|
134
|
+
store = new Store(getDefaultAsyncRetryerState());
|
|
135
|
+
key;
|
|
136
|
+
options;
|
|
137
|
+
#abortController = null;
|
|
134
138
|
/**
|
|
135
139
|
* Creates a new AsyncRetryer instance
|
|
136
140
|
* @param fn The async function to retry
|
|
@@ -138,175 +142,6 @@ var AsyncRetryer = class {
|
|
|
138
142
|
*/
|
|
139
143
|
constructor(fn, initialOptions = {}) {
|
|
140
144
|
this.fn = fn;
|
|
141
|
-
this.store = new Store(getDefaultAsyncRetryerState());
|
|
142
|
-
this.#abortController = null;
|
|
143
|
-
this.setOptions = (newOptions) => {
|
|
144
|
-
this.options = {
|
|
145
|
-
...this.options,
|
|
146
|
-
...newOptions
|
|
147
|
-
};
|
|
148
|
-
};
|
|
149
|
-
this.#setState = (newState) => {
|
|
150
|
-
this.store.setState((state) => {
|
|
151
|
-
const combinedState = {
|
|
152
|
-
...state,
|
|
153
|
-
...newState
|
|
154
|
-
};
|
|
155
|
-
const { isExecuting, currentAttempt } = combinedState;
|
|
156
|
-
return {
|
|
157
|
-
...combinedState,
|
|
158
|
-
status: !this.#getEnabled() ? "disabled" : isExecuting && currentAttempt === 1 ? "executing" : isExecuting && currentAttempt > 1 ? "retrying" : "idle"
|
|
159
|
-
};
|
|
160
|
-
});
|
|
161
|
-
};
|
|
162
|
-
this.#getEnabled = () => {
|
|
163
|
-
return !!parseFunctionOrValue(this.options.enabled, this);
|
|
164
|
-
};
|
|
165
|
-
this.#getMaxAttempts = () => {
|
|
166
|
-
return parseFunctionOrValue(this.options.maxAttempts, this);
|
|
167
|
-
};
|
|
168
|
-
this.#getBaseWait = () => {
|
|
169
|
-
return parseFunctionOrValue(this.options.baseWait, this);
|
|
170
|
-
};
|
|
171
|
-
this.#getMaxWait = () => {
|
|
172
|
-
return parseFunctionOrValue(this.options.maxWait, this);
|
|
173
|
-
};
|
|
174
|
-
this.#calculateJitter = (waitTime) => {
|
|
175
|
-
const jitterAmount = this.options.jitter;
|
|
176
|
-
if (jitterAmount <= 0) return 0;
|
|
177
|
-
try {
|
|
178
|
-
const crypto = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
|
|
179
|
-
if (crypto?.getRandomValues) {
|
|
180
|
-
const array = /* @__PURE__ */ new Uint32Array(1);
|
|
181
|
-
crypto.getRandomValues(array);
|
|
182
|
-
const randomFactor = array[0] / 4294967295 * 2 - 1;
|
|
183
|
-
return Math.floor(waitTime * jitterAmount * randomFactor);
|
|
184
|
-
}
|
|
185
|
-
} catch {}
|
|
186
|
-
return 0;
|
|
187
|
-
};
|
|
188
|
-
this.#calculateWait = (attempt) => {
|
|
189
|
-
const baseWait = this.#getBaseWait();
|
|
190
|
-
let waitTime;
|
|
191
|
-
switch (this.options.backoff) {
|
|
192
|
-
case "linear":
|
|
193
|
-
waitTime = baseWait * attempt;
|
|
194
|
-
break;
|
|
195
|
-
case "exponential":
|
|
196
|
-
waitTime = baseWait * Math.pow(2, attempt - 1);
|
|
197
|
-
break;
|
|
198
|
-
default: waitTime = baseWait;
|
|
199
|
-
}
|
|
200
|
-
waitTime = Math.min(waitTime, this.#getMaxWait());
|
|
201
|
-
const jitter = this.#calculateJitter(waitTime);
|
|
202
|
-
return Math.max(0, waitTime + jitter);
|
|
203
|
-
};
|
|
204
|
-
this.execute = async (...args) => {
|
|
205
|
-
if (!this.#getEnabled()) return;
|
|
206
|
-
this.abort("new-execution");
|
|
207
|
-
const startTime = Date.now();
|
|
208
|
-
let lastError;
|
|
209
|
-
let result;
|
|
210
|
-
this.#abortController = new AbortController();
|
|
211
|
-
const signal = this.#abortController.signal;
|
|
212
|
-
this.#setState({
|
|
213
|
-
isExecuting: true,
|
|
214
|
-
currentAttempt: 0,
|
|
215
|
-
lastError: void 0
|
|
216
|
-
});
|
|
217
|
-
let totalTimeoutId;
|
|
218
|
-
if (this.options.maxTotalExecutionTime !== Infinity) totalTimeoutId = setTimeout(() => {
|
|
219
|
-
this.options.onTotalExecutionTimeout?.(this);
|
|
220
|
-
this.abort("total-timeout");
|
|
221
|
-
}, this.options.maxTotalExecutionTime);
|
|
222
|
-
let isLastAttempt = false;
|
|
223
|
-
for (let attempt = 1; attempt <= this.#getMaxAttempts(); attempt++) {
|
|
224
|
-
isLastAttempt = attempt === this.#getMaxAttempts();
|
|
225
|
-
this.#setState({ currentAttempt: attempt });
|
|
226
|
-
try {
|
|
227
|
-
if (signal.aborted) return;
|
|
228
|
-
const currentTotalTime = Date.now() - startTime;
|
|
229
|
-
if (this.options.maxTotalExecutionTime !== Infinity && currentTotalTime >= this.options.maxTotalExecutionTime) {
|
|
230
|
-
this.options.onTotalExecutionTimeout?.(this);
|
|
231
|
-
this.abort("total-timeout");
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (this.options.maxExecutionTime === Infinity) result = await this.fn(...args);
|
|
235
|
-
else result = await Promise.race([this.fn(...args), new Promise((_, reject) => {
|
|
236
|
-
const timeout = setTimeout(() => {
|
|
237
|
-
this.options.onExecutionTimeout?.(this);
|
|
238
|
-
this.abort("execution-timeout");
|
|
239
|
-
reject(/* @__PURE__ */ new Error(`Execution timeout: ${this.options.maxExecutionTime}ms exceeded`));
|
|
240
|
-
}, this.options.maxExecutionTime);
|
|
241
|
-
signal.addEventListener("abort", () => {
|
|
242
|
-
clearTimeout(timeout);
|
|
243
|
-
reject(/* @__PURE__ */ new Error("Aborted"));
|
|
244
|
-
}, { once: true });
|
|
245
|
-
})]);
|
|
246
|
-
if (signal.aborted) return;
|
|
247
|
-
const totalTime = Date.now() - startTime;
|
|
248
|
-
this.#setState({
|
|
249
|
-
executionCount: this.store.state.executionCount + 1,
|
|
250
|
-
isExecuting: false,
|
|
251
|
-
lastExecutionTime: Date.now(),
|
|
252
|
-
totalExecutionTime: totalTime,
|
|
253
|
-
currentAttempt: 0,
|
|
254
|
-
lastResult: result
|
|
255
|
-
});
|
|
256
|
-
this.options.onSuccess?.(result, args, this);
|
|
257
|
-
return result;
|
|
258
|
-
} catch (error) {
|
|
259
|
-
if (error && typeof error === "object" && "name" in error && error.name === "AbortError") return;
|
|
260
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
261
|
-
this.#setState({ lastError });
|
|
262
|
-
this.options.onError?.(lastError, args, this);
|
|
263
|
-
if (attempt < this.#getMaxAttempts()) {
|
|
264
|
-
this.options.onRetry?.(attempt, lastError, this);
|
|
265
|
-
const wait = this.#calculateWait(attempt);
|
|
266
|
-
if (wait > 0) {
|
|
267
|
-
this.#setState({
|
|
268
|
-
isExecuting: true,
|
|
269
|
-
currentAttempt: attempt + 1
|
|
270
|
-
});
|
|
271
|
-
await new Promise((resolve) => {
|
|
272
|
-
const timeout = setTimeout(() => {
|
|
273
|
-
signal.removeEventListener("abort", onAbort);
|
|
274
|
-
resolve();
|
|
275
|
-
}, wait);
|
|
276
|
-
const onAbort = () => {
|
|
277
|
-
clearTimeout(timeout);
|
|
278
|
-
signal.removeEventListener("abort", onAbort);
|
|
279
|
-
resolve();
|
|
280
|
-
};
|
|
281
|
-
signal.addEventListener("abort", onAbort);
|
|
282
|
-
});
|
|
283
|
-
if (signal.aborted) return;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
} finally {
|
|
287
|
-
this.options.onSettled?.(args, this);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
if (totalTimeoutId) clearTimeout(totalTimeoutId);
|
|
291
|
-
this.#setState({ isExecuting: false });
|
|
292
|
-
this.options.onLastError?.(lastError, this);
|
|
293
|
-
this.options.onSettled?.(args, this);
|
|
294
|
-
if (this.options.throwOnError === "last" && isLastAttempt || this.options.throwOnError === true) throw lastError;
|
|
295
|
-
};
|
|
296
|
-
this.getAbortSignal = () => {
|
|
297
|
-
return this.#abortController?.signal ?? null;
|
|
298
|
-
};
|
|
299
|
-
this.abort = (reason = "manual") => {
|
|
300
|
-
if (this.#abortController) {
|
|
301
|
-
this.#abortController.abort();
|
|
302
|
-
this.#abortController = null;
|
|
303
|
-
this.#setState({ isExecuting: false });
|
|
304
|
-
this.options.onAbort?.(reason, this);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
this.reset = () => {
|
|
308
|
-
this.#setState(getDefaultAsyncRetryerState());
|
|
309
|
-
};
|
|
310
145
|
this.key = initialOptions.key;
|
|
311
146
|
this.options = {
|
|
312
147
|
...defaultOptions,
|
|
@@ -315,13 +150,209 @@ var AsyncRetryer = class {
|
|
|
315
150
|
};
|
|
316
151
|
this.#setState(this.options.initialState ?? {});
|
|
317
152
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Updates the retryer options
|
|
155
|
+
* @param newOptions Partial options to merge with existing options
|
|
156
|
+
*/
|
|
157
|
+
setOptions = (newOptions) => {
|
|
158
|
+
this.options = {
|
|
159
|
+
...this.options,
|
|
160
|
+
...newOptions
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
#setState = (newState) => {
|
|
164
|
+
this.store.setState((state) => {
|
|
165
|
+
const combinedState = {
|
|
166
|
+
...state,
|
|
167
|
+
...newState
|
|
168
|
+
};
|
|
169
|
+
const { isExecuting, currentAttempt } = combinedState;
|
|
170
|
+
return {
|
|
171
|
+
...combinedState,
|
|
172
|
+
status: !this.#getEnabled() ? "disabled" : isExecuting && currentAttempt === 1 ? "executing" : isExecuting && currentAttempt > 1 ? "retrying" : "idle"
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
#getEnabled = () => {
|
|
177
|
+
return !!parseFunctionOrValue(this.options.enabled, this);
|
|
178
|
+
};
|
|
179
|
+
#getMaxAttempts = () => {
|
|
180
|
+
return parseFunctionOrValue(this.options.maxAttempts, this);
|
|
181
|
+
};
|
|
182
|
+
#getBaseWait = () => {
|
|
183
|
+
return parseFunctionOrValue(this.options.baseWait, this);
|
|
184
|
+
};
|
|
185
|
+
#getMaxWait = () => {
|
|
186
|
+
return parseFunctionOrValue(this.options.maxWait, this);
|
|
187
|
+
};
|
|
188
|
+
#calculateJitter = (waitTime) => {
|
|
189
|
+
const jitterAmount = this.options.jitter;
|
|
190
|
+
if (jitterAmount <= 0) return 0;
|
|
191
|
+
try {
|
|
192
|
+
const crypto = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
|
|
193
|
+
if (crypto?.getRandomValues) {
|
|
194
|
+
const array = /* @__PURE__ */ new Uint32Array(1);
|
|
195
|
+
crypto.getRandomValues(array);
|
|
196
|
+
const randomFactor = array[0] / 4294967295 * 2 - 1;
|
|
197
|
+
return Math.floor(waitTime * jitterAmount * randomFactor);
|
|
198
|
+
}
|
|
199
|
+
} catch {}
|
|
200
|
+
return 0;
|
|
201
|
+
};
|
|
202
|
+
#calculateWait = (attempt) => {
|
|
203
|
+
const baseWait = this.#getBaseWait();
|
|
204
|
+
let waitTime;
|
|
205
|
+
switch (this.options.backoff) {
|
|
206
|
+
case "linear":
|
|
207
|
+
waitTime = baseWait * attempt;
|
|
208
|
+
break;
|
|
209
|
+
case "exponential":
|
|
210
|
+
waitTime = baseWait * Math.pow(2, attempt - 1);
|
|
211
|
+
break;
|
|
212
|
+
default: waitTime = baseWait;
|
|
213
|
+
}
|
|
214
|
+
waitTime = Math.min(waitTime, this.#getMaxWait());
|
|
215
|
+
const jitter = this.#calculateJitter(waitTime);
|
|
216
|
+
return Math.max(0, waitTime + jitter);
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Executes the function with retry logic
|
|
220
|
+
* @param args Arguments to pass to the function
|
|
221
|
+
* @returns The function result, or undefined if disabled or all retries failed (when throwOnError is false)
|
|
222
|
+
* @throws The last error if throwOnError is true and all retries fail
|
|
223
|
+
*/
|
|
224
|
+
execute = async (...args) => {
|
|
225
|
+
if (!this.#getEnabled()) return;
|
|
226
|
+
this.abort("new-execution");
|
|
227
|
+
const startTime = Date.now();
|
|
228
|
+
let lastError;
|
|
229
|
+
let result;
|
|
230
|
+
this.#abortController = new AbortController();
|
|
231
|
+
const signal = this.#abortController.signal;
|
|
232
|
+
this.#setState({
|
|
233
|
+
isExecuting: true,
|
|
234
|
+
currentAttempt: 0,
|
|
235
|
+
lastError: void 0
|
|
236
|
+
});
|
|
237
|
+
let totalTimeoutId;
|
|
238
|
+
if (this.options.maxTotalExecutionTime !== Infinity) totalTimeoutId = setTimeout(() => {
|
|
239
|
+
this.options.onTotalExecutionTimeout?.(this);
|
|
240
|
+
this.abort("total-timeout");
|
|
241
|
+
}, this.options.maxTotalExecutionTime);
|
|
242
|
+
let isLastAttempt = false;
|
|
243
|
+
for (let attempt = 1; attempt <= this.#getMaxAttempts(); attempt++) {
|
|
244
|
+
isLastAttempt = attempt === this.#getMaxAttempts();
|
|
245
|
+
this.#setState({ currentAttempt: attempt });
|
|
246
|
+
try {
|
|
247
|
+
if (signal.aborted) return;
|
|
248
|
+
const currentTotalTime = Date.now() - startTime;
|
|
249
|
+
if (this.options.maxTotalExecutionTime !== Infinity && currentTotalTime >= this.options.maxTotalExecutionTime) {
|
|
250
|
+
this.options.onTotalExecutionTimeout?.(this);
|
|
251
|
+
this.abort("total-timeout");
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (this.options.maxExecutionTime === Infinity) result = await this.fn(...args);
|
|
255
|
+
else result = await Promise.race([this.fn(...args), new Promise((_, reject) => {
|
|
256
|
+
const timeout = setTimeout(() => {
|
|
257
|
+
this.options.onExecutionTimeout?.(this);
|
|
258
|
+
this.abort("execution-timeout");
|
|
259
|
+
reject(/* @__PURE__ */ new Error(`Execution timeout: ${this.options.maxExecutionTime}ms exceeded`));
|
|
260
|
+
}, this.options.maxExecutionTime);
|
|
261
|
+
signal.addEventListener("abort", () => {
|
|
262
|
+
clearTimeout(timeout);
|
|
263
|
+
reject(/* @__PURE__ */ new Error("Aborted"));
|
|
264
|
+
}, { once: true });
|
|
265
|
+
})]);
|
|
266
|
+
if (signal.aborted) return;
|
|
267
|
+
const totalTime = Date.now() - startTime;
|
|
268
|
+
this.#setState({
|
|
269
|
+
executionCount: this.store.state.executionCount + 1,
|
|
270
|
+
isExecuting: false,
|
|
271
|
+
lastExecutionTime: Date.now(),
|
|
272
|
+
totalExecutionTime: totalTime,
|
|
273
|
+
currentAttempt: 0,
|
|
274
|
+
lastResult: result
|
|
275
|
+
});
|
|
276
|
+
this.options.onSuccess?.(result, args, this);
|
|
277
|
+
return result;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (error && typeof error === "object" && "name" in error && error.name === "AbortError") return;
|
|
280
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
281
|
+
this.#setState({ lastError });
|
|
282
|
+
this.options.onError?.(lastError, args, this);
|
|
283
|
+
if (attempt < this.#getMaxAttempts()) {
|
|
284
|
+
this.options.onRetry?.(attempt, lastError, this);
|
|
285
|
+
const wait = this.#calculateWait(attempt);
|
|
286
|
+
if (wait > 0) {
|
|
287
|
+
this.#setState({
|
|
288
|
+
isExecuting: true,
|
|
289
|
+
currentAttempt: attempt + 1
|
|
290
|
+
});
|
|
291
|
+
await new Promise((resolve) => {
|
|
292
|
+
const timeout = setTimeout(() => {
|
|
293
|
+
signal.removeEventListener("abort", onAbort);
|
|
294
|
+
resolve();
|
|
295
|
+
}, wait);
|
|
296
|
+
const onAbort = () => {
|
|
297
|
+
clearTimeout(timeout);
|
|
298
|
+
signal.removeEventListener("abort", onAbort);
|
|
299
|
+
resolve();
|
|
300
|
+
};
|
|
301
|
+
signal.addEventListener("abort", onAbort);
|
|
302
|
+
});
|
|
303
|
+
if (signal.aborted) return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} finally {
|
|
307
|
+
this.options.onSettled?.(args, this);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (totalTimeoutId) clearTimeout(totalTimeoutId);
|
|
311
|
+
this.#setState({ isExecuting: false });
|
|
312
|
+
this.options.onLastError?.(lastError, this);
|
|
313
|
+
this.options.onSettled?.(args, this);
|
|
314
|
+
if (this.options.throwOnError === "last" && isLastAttempt || this.options.throwOnError === true) throw lastError;
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Returns the current AbortSignal for the executing operation.
|
|
318
|
+
* Use this signal in your async function to make it cancellable.
|
|
319
|
+
* Returns null when not currently executing.
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```typescript
|
|
323
|
+
* const retryer = new AsyncRetryer(async (userId: string) => {
|
|
324
|
+
* const signal = retryer.getAbortSignal()
|
|
325
|
+
* if (signal) {
|
|
326
|
+
* return fetch(`/api/users/${userId}`, { signal })
|
|
327
|
+
* }
|
|
328
|
+
* return fetch(`/api/users/${userId}`)
|
|
329
|
+
* })
|
|
330
|
+
*
|
|
331
|
+
* // Abort will now actually cancel the fetch
|
|
332
|
+
* retryer.abort()
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
getAbortSignal = () => {
|
|
336
|
+
return this.#abortController?.signal ?? null;
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* Cancels the current execution and any pending retries
|
|
340
|
+
* @param reason The reason for the abort (defaults to 'manual')
|
|
341
|
+
*/
|
|
342
|
+
abort = (reason = "manual") => {
|
|
343
|
+
if (this.#abortController) {
|
|
344
|
+
this.#abortController.abort();
|
|
345
|
+
this.#abortController = null;
|
|
346
|
+
this.#setState({ isExecuting: false });
|
|
347
|
+
this.options.onAbort?.(reason, this);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* Resets the retryer to its initial state
|
|
352
|
+
*/
|
|
353
|
+
reset = () => {
|
|
354
|
+
this.#setState(getDefaultAsyncRetryerState());
|
|
355
|
+
};
|
|
325
356
|
};
|
|
326
357
|
/**
|
|
327
358
|
* Creates a retry-enabled version of an async function. This is a convenience wrapper
|
|
@@ -358,5 +389,4 @@ function asyncRetry(fn, initialOptions = {}) {
|
|
|
358
389
|
}
|
|
359
390
|
|
|
360
391
|
//#endregion
|
|
361
|
-
export { AsyncRetryer, asyncRetry, asyncRetryerOptions };
|
|
362
|
-
//# sourceMappingURL=async-retryer.js.map
|
|
392
|
+
export { AsyncRetryer, asyncRetry, asyncRetryerOptions };
|
|
@@ -2,7 +2,7 @@ import { AnyAsyncFunction } from "./types.js";
|
|
|
2
2
|
import { AsyncRetryer, AsyncRetryerOptions } from "./async-retryer.js";
|
|
3
3
|
import { Store } from "@tanstack/store";
|
|
4
4
|
//#region src/async-throttler.d.ts
|
|
5
|
-
interface AsyncThrottlerState<TFn extends AnyAsyncFunction> {
|
|
5
|
+
export interface AsyncThrottlerState<TFn extends AnyAsyncFunction> {
|
|
6
6
|
/**
|
|
7
7
|
* Number of function executions that have resulted in errors
|
|
8
8
|
*/
|
|
@@ -51,7 +51,7 @@ interface AsyncThrottlerState<TFn extends AnyAsyncFunction> {
|
|
|
51
51
|
/**
|
|
52
52
|
* Options for configuring an async throttled function
|
|
53
53
|
*/
|
|
54
|
-
interface AsyncThrottlerOptions<TFn extends AnyAsyncFunction> {
|
|
54
|
+
export interface AsyncThrottlerOptions<TFn extends AnyAsyncFunction> {
|
|
55
55
|
/**
|
|
56
56
|
* Options for configuring the underlying async retryer
|
|
57
57
|
*/
|
|
@@ -111,7 +111,7 @@ interface AsyncThrottlerOptions<TFn extends AnyAsyncFunction> {
|
|
|
111
111
|
/**
|
|
112
112
|
* Utility function for sharing common `AsyncThrottlerOptions` options between different `AsyncThrottler` instances.
|
|
113
113
|
*/
|
|
114
|
-
declare function asyncThrottlerOptions<TFn extends AnyAsyncFunction = AnyAsyncFunction, TOptions extends Partial<AsyncThrottlerOptions<TFn>> = Partial<AsyncThrottlerOptions<TFn>>>(options: TOptions): TOptions;
|
|
114
|
+
export declare function asyncThrottlerOptions<TFn extends AnyAsyncFunction = AnyAsyncFunction, TOptions extends Partial<AsyncThrottlerOptions<TFn>> = Partial<AsyncThrottlerOptions<TFn>>>(options: TOptions): TOptions;
|
|
115
115
|
/**
|
|
116
116
|
* A class that creates an async throttled function.
|
|
117
117
|
*
|
|
@@ -170,7 +170,7 @@ declare function asyncThrottlerOptions<TFn extends AnyAsyncFunction = AnyAsyncFu
|
|
|
170
170
|
* const result = await throttler.maybeExecute(inputElement.value);
|
|
171
171
|
* ```
|
|
172
172
|
*/
|
|
173
|
-
declare class AsyncThrottler<TFn extends AnyAsyncFunction> {
|
|
173
|
+
export declare class AsyncThrottler<TFn extends AnyAsyncFunction> {
|
|
174
174
|
#private;
|
|
175
175
|
fn: TFn;
|
|
176
176
|
readonly store: Store<Readonly<AsyncThrottlerState<TFn>>>;
|
|
@@ -313,7 +313,5 @@ declare class AsyncThrottler<TFn extends AnyAsyncFunction> {
|
|
|
313
313
|
* const result = await throttled(inputElement.value);
|
|
314
314
|
* ```
|
|
315
315
|
*/
|
|
316
|
-
declare function asyncThrottle<TFn extends AnyAsyncFunction>(fn: TFn, initialOptions: AsyncThrottlerOptions<TFn>): (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
|
|
317
|
-
//#endregion
|
|
318
|
-
export { AsyncThrottler, AsyncThrottlerOptions, AsyncThrottlerState, asyncThrottle, asyncThrottlerOptions };
|
|
319
|
-
//# sourceMappingURL=async-throttler.d.ts.map
|
|
316
|
+
export declare function asyncThrottle<TFn extends AnyAsyncFunction>(fn: TFn, initialOptions: AsyncThrottlerOptions<TFn>): (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
|
|
317
|
+
//#endregion
|