@tanstack/pacer 0.6.0 → 0.7.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.
Files changed (71) hide show
  1. package/dist/cjs/async-debouncer.cjs +10 -6
  2. package/dist/cjs/async-debouncer.cjs.map +1 -1
  3. package/dist/cjs/async-debouncer.d.cts +2 -2
  4. package/dist/cjs/async-queuer.cjs +135 -106
  5. package/dist/cjs/async-queuer.cjs.map +1 -1
  6. package/dist/cjs/async-queuer.d.cts +121 -93
  7. package/dist/cjs/async-rate-limiter.cjs +3 -4
  8. package/dist/cjs/async-rate-limiter.cjs.map +1 -1
  9. package/dist/cjs/async-rate-limiter.d.cts +1 -2
  10. package/dist/cjs/async-throttler.cjs +14 -4
  11. package/dist/cjs/async-throttler.cjs.map +1 -1
  12. package/dist/cjs/async-throttler.d.cts +3 -2
  13. package/dist/cjs/batcher.cjs +138 -0
  14. package/dist/cjs/batcher.cjs.map +1 -0
  15. package/dist/cjs/batcher.d.cts +149 -0
  16. package/dist/cjs/debouncer.cjs +3 -4
  17. package/dist/cjs/debouncer.cjs.map +1 -1
  18. package/dist/cjs/debouncer.d.cts +1 -2
  19. package/dist/cjs/index.cjs +3 -0
  20. package/dist/cjs/index.cjs.map +1 -1
  21. package/dist/cjs/index.d.cts +1 -0
  22. package/dist/cjs/queuer.cjs +68 -41
  23. package/dist/cjs/queuer.cjs.map +1 -1
  24. package/dist/cjs/queuer.d.cts +123 -92
  25. package/dist/cjs/rate-limiter.cjs +3 -4
  26. package/dist/cjs/rate-limiter.cjs.map +1 -1
  27. package/dist/cjs/rate-limiter.d.cts +1 -2
  28. package/dist/cjs/throttler.cjs +3 -4
  29. package/dist/cjs/throttler.cjs.map +1 -1
  30. package/dist/cjs/throttler.d.cts +1 -2
  31. package/dist/esm/async-debouncer.d.ts +2 -2
  32. package/dist/esm/async-debouncer.js +10 -6
  33. package/dist/esm/async-debouncer.js.map +1 -1
  34. package/dist/esm/async-queuer.d.ts +121 -93
  35. package/dist/esm/async-queuer.js +135 -106
  36. package/dist/esm/async-queuer.js.map +1 -1
  37. package/dist/esm/async-rate-limiter.d.ts +1 -2
  38. package/dist/esm/async-rate-limiter.js +3 -4
  39. package/dist/esm/async-rate-limiter.js.map +1 -1
  40. package/dist/esm/async-throttler.d.ts +3 -2
  41. package/dist/esm/async-throttler.js +14 -4
  42. package/dist/esm/async-throttler.js.map +1 -1
  43. package/dist/esm/batcher.d.ts +149 -0
  44. package/dist/esm/batcher.js +138 -0
  45. package/dist/esm/batcher.js.map +1 -0
  46. package/dist/esm/debouncer.d.ts +1 -2
  47. package/dist/esm/debouncer.js +3 -4
  48. package/dist/esm/debouncer.js.map +1 -1
  49. package/dist/esm/index.d.ts +1 -0
  50. package/dist/esm/index.js +3 -0
  51. package/dist/esm/index.js.map +1 -1
  52. package/dist/esm/queuer.d.ts +123 -92
  53. package/dist/esm/queuer.js +68 -41
  54. package/dist/esm/queuer.js.map +1 -1
  55. package/dist/esm/rate-limiter.d.ts +1 -2
  56. package/dist/esm/rate-limiter.js +3 -4
  57. package/dist/esm/rate-limiter.js.map +1 -1
  58. package/dist/esm/throttler.d.ts +1 -2
  59. package/dist/esm/throttler.js +3 -4
  60. package/dist/esm/throttler.js.map +1 -1
  61. package/package.json +11 -1
  62. package/src/async-debouncer.ts +12 -6
  63. package/src/async-queuer.ts +216 -193
  64. package/src/async-rate-limiter.ts +3 -4
  65. package/src/async-throttler.ts +18 -4
  66. package/src/batcher.ts +253 -0
  67. package/src/debouncer.ts +3 -4
  68. package/src/index.ts +1 -0
  69. package/src/queuer.ts +142 -98
  70. package/src/rate-limiter.ts +3 -4
  71. package/src/throttler.ts +3 -4
@@ -18,6 +18,7 @@ class AsyncDebouncer {
18
18
  this._settleCount = 0;
19
19
  this._successCount = 0;
20
20
  this._timeoutId = null;
21
+ this._resolvePreviousPromise = null;
21
22
  this._options = {
22
23
  ...defaultOptions,
23
24
  ...initialOptions,
@@ -26,7 +27,6 @@ class AsyncDebouncer {
26
27
  }
27
28
  /**
28
29
  * Updates the debouncer options
29
- * Returns the new options state
30
30
  */
31
31
  setOptions(newOptions) {
32
32
  this._options = { ...this._options, ...newOptions };
@@ -71,23 +71,25 @@ class AsyncDebouncer {
71
71
  this._lastArgs = args;
72
72
  if (this._options.leading && this._canLeadingExecute) {
73
73
  this._canLeadingExecute = false;
74
- await this.executeFunction(...args);
74
+ await this.execute(...args);
75
75
  return this._lastResult;
76
76
  }
77
77
  if (this._options.trailing) {
78
78
  this._isPending = true;
79
79
  }
80
80
  return new Promise((resolve) => {
81
+ this._resolvePreviousPromise = resolve;
81
82
  this._timeoutId = setTimeout(async () => {
82
83
  if (this._options.trailing && this._lastArgs) {
83
- await this.executeFunction(...this._lastArgs);
84
+ await this.execute(...this._lastArgs);
84
85
  }
85
86
  this._canLeadingExecute = true;
87
+ this._resolvePreviousPromise = null;
86
88
  resolve(this._lastResult);
87
89
  }, this.getWait());
88
90
  });
89
91
  }
90
- async executeFunction(...args) {
92
+ async execute(...args) {
91
93
  var _a, _b, _c, _d, _e, _f;
92
94
  if (!this.getEnabled()) return void 0;
93
95
  this._abortController = new AbortController();
@@ -101,8 +103,6 @@ class AsyncDebouncer {
101
103
  (_d = (_c = this._options).onError) == null ? void 0 : _d.call(_c, error, this);
102
104
  if (this._options.throwOnError) {
103
105
  throw error;
104
- } else {
105
- console.error(error);
106
106
  }
107
107
  } finally {
108
108
  this._isExecuting = false;
@@ -125,6 +125,10 @@ class AsyncDebouncer {
125
125
  this._abortController.abort();
126
126
  this._abortController = null;
127
127
  }
128
+ if (this._resolvePreviousPromise) {
129
+ this._resolvePreviousPromise(this._lastResult);
130
+ this._resolvePreviousPromise = null;
131
+ }
128
132
  this._lastArgs = void 0;
129
133
  this._isPending = false;
130
134
  this._isExecuting = false;
@@ -1 +1 @@
1
- {"version":3,"file":"async-debouncer.cjs","sources":["../../src/async-debouncer.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\nimport type { AnyAsyncFunction, OptionalKeys } from './types'\n\n/**\n * Options for configuring an async debounced function\n */\nexport interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Can be a boolean or a function that returns a boolean.\n * Defaults to true.\n */\n enabled?: boolean | ((debouncer: AsyncDebouncer<TFn>) => boolean)\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Optional error handler for when the debounced function throws.\n * If provided, the handler will be called with the error and debouncer instance.\n * This can be used alongside throwOnError - the handler will be called before any error is thrown.\n */\n onError?: (error: unknown, debouncer: AsyncDebouncer<TFn>) => void\n /**\n * Optional callback to call when the debounced function is executed\n */\n onSettled?: (debouncer: AsyncDebouncer<TFn>) => void\n /**\n * Optional callback to call when the debounced function is executed\n */\n onSuccess?: (result: ReturnType<TFn>, debouncer: AsyncDebouncer<TFn>) => void\n /**\n * Whether to throw errors when they occur.\n * Defaults to true if no onError handler is provided, false if an onError handler is provided.\n * Can be explicitly set to override these defaults.\n */\n throwOnError?: 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 to wait after the last call before executing.\n * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((debouncer: AsyncDebouncer<TFn>) => number)\n}\n\ntype AsyncDebouncerOptionsWithOptionalCallbacks = OptionalKeys<\n AsyncDebouncerOptions<any>,\n 'onError' | 'onSettled' | 'onSuccess'\n>\n\nconst defaultOptions: AsyncDebouncerOptionsWithOptionalCallbacks = {\n enabled: true,\n leading: false,\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates an async debounced function.\n *\n * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.\n * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing\n * or input changes where you only want to execute the handler after the events have stopped occurring.\n *\n * Unlike throttling which allows execution at regular intervals, debouncing prevents any execution until\n * the function stops being called for the specified delay period.\n *\n * Unlike the non-async Debouncer, this async version supports returning values from the debounced function,\n * making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call\n * instead of setting the result on a state variable from within the debounced function.\n *\n * Error Handling:\n * - If an error occurs during execution and no `onError` handler is provided, the error will be thrown and propagate up to the caller.\n * - If an `onError` handler is provided, errors will be caught and passed to the handler instead of being thrown.\n * - The error count can be tracked using `getErrorCount()`.\n * - The debouncer maintains its state and can continue to be used after an error occurs.\n *\n * @example\n * ```ts\n * const asyncDebouncer = new AsyncDebouncer(async (value: string) => {\n * const results = await searchAPI(value);\n * return results; // Return value is preserved\n * }, {\n * wait: 500,\n * onError: (error) => {\n * console.error('Search failed:', error);\n * }\n * });\n *\n * // Called on each keystroke but only executes after 500ms of no typing\n * // Returns the API response directly\n * const results = await asyncDebouncer.maybeExecute(inputElement.value);\n * ```\n */\nexport class AsyncDebouncer<TFn extends AnyAsyncFunction> {\n private _options: AsyncDebouncerOptionsWithOptionalCallbacks\n private _abortController: AbortController | null = null\n private _canLeadingExecute = true\n private _errorCount = 0\n private _isExecuting = false\n private _isPending = false\n private _lastArgs: Parameters<TFn> | undefined\n private _lastResult: ReturnType<TFn> | undefined\n private _settleCount = 0\n private _successCount = 0\n private _timeoutId: NodeJS.Timeout | null = null\n\n constructor(\n private fn: TFn,\n initialOptions: AsyncDebouncerOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n throwOnError: initialOptions.throwOnError ?? !initialOptions.onError,\n }\n }\n\n /**\n * Updates the debouncer options\n * Returns the new options state\n */\n setOptions(newOptions: Partial<AsyncDebouncerOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this._isPending = false\n }\n }\n\n /**\n * Returns the current debouncer options\n */\n getOptions(): AsyncDebouncerOptions<TFn> {\n return this._options\n }\n\n /**\n * Returns the current debouncer enabled state\n */\n getEnabled(): boolean {\n return !!parseFunctionOrValue(this._options.enabled, this)\n }\n\n /**\n * Returns the current debouncer wait state\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Attempts to execute the debounced function.\n * If a call is already in progress, it will be queued.\n *\n * Error Handling:\n * - If the debounced function throws and no `onError` handler is configured,\n * the error will be thrown from this method.\n * - If an `onError` handler is configured, errors will be caught and passed to the handler,\n * and this method will return undefined.\n * - The error state can be checked using `getErrorCount()` and `getIsExecuting()`.\n *\n * @returns A promise that resolves with the function's return value, or undefined if an error occurred and was handled by onError\n * @throws The error from the debounced function if no onError handler is configured\n */\n async maybeExecute(\n ...args: Parameters<TFn>\n ): Promise<ReturnType<TFn> | undefined> {\n this._cancel()\n this._lastArgs = args\n\n // Handle leading execution\n if (this._options.leading && this._canLeadingExecute) {\n this._canLeadingExecute = false\n await this.executeFunction(...args)\n return this._lastResult\n }\n\n // Handle trailing execution\n if (this._options.trailing) {\n this._isPending = true\n }\n\n return new Promise((resolve) => {\n this._timeoutId = setTimeout(async () => {\n // Execute trailing if enabled\n if (this._options.trailing && this._lastArgs) {\n await this.executeFunction(...this._lastArgs)\n }\n\n // Reset state and resolve\n this._canLeadingExecute = true\n resolve(this._lastResult)\n }, this.getWait())\n })\n }\n\n private async executeFunction(\n ...args: Parameters<TFn>\n ): Promise<ReturnType<TFn> | undefined> {\n if (!this.getEnabled()) return undefined\n this._abortController = new AbortController()\n try {\n this._isExecuting = true\n this._lastResult = await this.fn(...args) // EXECUTE!\n this._successCount++\n this._options.onSuccess?.(this._lastResult!, this)\n } catch (error) {\n this._errorCount++\n this._options.onError?.(error, this)\n if (this._options.throwOnError) {\n throw error\n } else {\n console.error(error)\n }\n } finally {\n this._isExecuting = false\n this._isPending = false\n this._settleCount++\n this._abortController = null\n this._options.onSettled?.(this)\n }\n return this._lastResult\n }\n\n /**\n * Cancel without resetting _canLeadingExecute\n */\n private _cancel(): void {\n if (this._timeoutId) {\n clearTimeout(this._timeoutId)\n this._timeoutId = null\n }\n if (this._abortController) {\n this._abortController.abort()\n this._abortController = null\n }\n this._lastArgs = undefined\n this._isPending = false\n this._isExecuting = false\n }\n\n /**\n * Cancels any pending execution or aborts any execution in progress\n */\n cancel(): void {\n this._canLeadingExecute = true\n this._cancel()\n }\n\n /**\n * Returns the last result of the debounced function\n */\n getLastResult(): ReturnType<TFn> | undefined {\n return this._lastResult\n }\n\n /**\n * Returns the number of times the function has been executed successfully\n */\n getSuccessCount(): number {\n return this._successCount\n }\n\n /**\n * Returns the number of times the function has settled (completed or errored)\n */\n getSettleCount(): number {\n return this._settleCount\n }\n\n /**\n * Returns the number of times the function has errored\n */\n getErrorCount(): number {\n return this._errorCount\n }\n\n /**\n * Returns `true` if there is a pending execution queued up for trailing execution\n */\n getIsPending(): boolean {\n return this.getEnabled() && this._isPending\n }\n\n /**\n * Returns `true` if there is currently an execution in progress\n */\n getIsExecuting(): boolean {\n return this._isExecuting\n }\n}\n\n/**\n * Creates an async debounced function that delays execution until after a specified wait time.\n * The debounced function will only execute once the wait period has elapsed without any new calls.\n * If called again during the wait period, the timer resets and a new wait period begins.\n *\n * Unlike the non-async Debouncer, this async version supports returning values from the debounced function,\n * making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call\n * instead of setting the result on a state variable from within the debounced function.\n *\n * Error Handling:\n * - If an `onError` handler is provided, it will be called with the error and debouncer instance\n * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown\n * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed\n * - The error state can be checked using the underlying AsyncDebouncer instance\n * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown\n *\n * @example\n * ```ts\n * const debounced = asyncDebounce(async (value: string) => {\n * const result = await saveToAPI(value);\n * return result; // Return value is preserved\n * }, {\n * wait: 1000,\n * onError: (error) => {\n * console.error('API call failed:', error);\n * },\n * throwOnError: true // Will both log the error and throw it\n * });\n *\n * // Will only execute once, 1 second after the last call\n * // Returns the API response directly\n * const result = await debounced(\"third\");\n * ```\n */\nexport function asyncDebounce<TFn extends AnyAsyncFunction>(\n fn: TFn,\n initialOptions: AsyncDebouncerOptions<TFn>,\n) {\n const asyncDebouncer = new AsyncDebouncer(fn, initialOptions)\n return asyncDebouncer.maybeExecute.bind(asyncDebouncer)\n}\n"],"names":["parseFunctionOrValue"],"mappings":"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"}
1
+ {"version":3,"file":"async-debouncer.cjs","sources":["../../src/async-debouncer.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\nimport type { AnyAsyncFunction, OptionalKeys } from './types'\n\n/**\n * Options for configuring an async debounced function\n */\nexport interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Can be a boolean or a function that returns a boolean.\n * Defaults to true.\n */\n enabled?: boolean | ((debouncer: AsyncDebouncer<TFn>) => boolean)\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Optional error handler for when the debounced function throws.\n * If provided, the handler will be called with the error and debouncer instance.\n * This can be used alongside throwOnError - the handler will be called before any error is thrown.\n */\n onError?: (error: unknown, debouncer: AsyncDebouncer<TFn>) => void\n /**\n * Optional callback to call when the debounced function is executed\n */\n onSettled?: (debouncer: AsyncDebouncer<TFn>) => void\n /**\n * Optional callback to call when the debounced function is executed\n */\n onSuccess?: (result: ReturnType<TFn>, debouncer: AsyncDebouncer<TFn>) => void\n /**\n * Whether to throw errors when they occur.\n * Defaults to true if no onError handler is provided, false if an onError handler is provided.\n * Can be explicitly set to override these defaults.\n */\n throwOnError?: 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 to wait after the last call before executing.\n * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((debouncer: AsyncDebouncer<TFn>) => number)\n}\n\ntype AsyncDebouncerOptionsWithOptionalCallbacks = OptionalKeys<\n AsyncDebouncerOptions<any>,\n 'onError' | 'onSettled' | 'onSuccess'\n>\n\nconst defaultOptions: AsyncDebouncerOptionsWithOptionalCallbacks = {\n enabled: true,\n leading: false,\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates an async debounced function.\n *\n * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.\n * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing\n * or input changes where you only want to execute the handler after the events have stopped occurring.\n *\n * Unlike throttling which allows execution at regular intervals, debouncing prevents any execution until\n * the function stops being called for the specified delay period.\n *\n * Unlike the non-async Debouncer, this async version supports returning values from the debounced function,\n * making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call\n * instead of setting the result on a state variable from within the debounced function.\n *\n * Error Handling:\n * - If an error occurs during execution and no `onError` handler is provided, the error will be thrown and propagate up to the caller.\n * - If an `onError` handler is provided, errors will be caught and passed to the handler instead of being thrown.\n * - The error count can be tracked using `getErrorCount()`.\n * - The debouncer maintains its state and can continue to be used after an error occurs.\n *\n * @example\n * ```ts\n * const asyncDebouncer = new AsyncDebouncer(async (value: string) => {\n * const results = await searchAPI(value);\n * return results; // Return value is preserved\n * }, {\n * wait: 500,\n * onError: (error) => {\n * console.error('Search failed:', error);\n * }\n * });\n *\n * // Called on each keystroke but only executes after 500ms of no typing\n * // Returns the API response directly\n * const results = await asyncDebouncer.maybeExecute(inputElement.value);\n * ```\n */\nexport class AsyncDebouncer<TFn extends AnyAsyncFunction> {\n private _options: AsyncDebouncerOptionsWithOptionalCallbacks\n private _abortController: AbortController | null = null\n private _canLeadingExecute = true\n private _errorCount = 0\n private _isExecuting = false\n private _isPending = false\n private _lastArgs: Parameters<TFn> | undefined\n private _lastResult: ReturnType<TFn> | undefined\n private _settleCount = 0\n private _successCount = 0\n private _timeoutId: NodeJS.Timeout | null = null\n private _resolvePreviousPromise:\n | ((value?: ReturnType<TFn> | undefined) => void)\n | null = null\n\n constructor(\n private fn: TFn,\n initialOptions: AsyncDebouncerOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n throwOnError: initialOptions.throwOnError ?? !initialOptions.onError,\n }\n }\n\n /**\n * Updates the debouncer options\n */\n setOptions(newOptions: Partial<AsyncDebouncerOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this._isPending = false\n }\n }\n\n /**\n * Returns the current debouncer options\n */\n getOptions(): AsyncDebouncerOptions<TFn> {\n return this._options\n }\n\n /**\n * Returns the current debouncer enabled state\n */\n getEnabled(): boolean {\n return !!parseFunctionOrValue(this._options.enabled, this)\n }\n\n /**\n * Returns the current debouncer wait state\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Attempts to execute the debounced function.\n * If a call is already in progress, it will be queued.\n *\n * Error Handling:\n * - If the debounced function throws and no `onError` handler is configured,\n * the error will be thrown from this method.\n * - If an `onError` handler is configured, errors will be caught and passed to the handler,\n * and this method will return undefined.\n * - The error state can be checked using `getErrorCount()` and `getIsExecuting()`.\n *\n * @returns A promise that resolves with the function's return value, or undefined if an error occurred and was handled by onError\n * @throws The error from the debounced function if no onError handler is configured\n */\n async maybeExecute(\n ...args: Parameters<TFn>\n ): Promise<ReturnType<TFn> | undefined> {\n this._cancel()\n this._lastArgs = args\n\n // Handle leading execution\n if (this._options.leading && this._canLeadingExecute) {\n this._canLeadingExecute = false\n await this.execute(...args)\n return this._lastResult\n }\n\n // Handle trailing execution\n if (this._options.trailing) {\n this._isPending = true\n }\n\n return new Promise((resolve) => {\n this._resolvePreviousPromise = resolve\n this._timeoutId = setTimeout(async () => {\n // Execute trailing if enabled\n if (this._options.trailing && this._lastArgs) {\n await this.execute(...this._lastArgs)\n }\n\n // Reset state and resolve\n this._canLeadingExecute = true\n this._resolvePreviousPromise = null\n resolve(this._lastResult)\n }, this.getWait())\n })\n }\n\n private async execute(\n ...args: Parameters<TFn>\n ): Promise<ReturnType<TFn> | undefined> {\n if (!this.getEnabled()) return undefined\n this._abortController = new AbortController()\n try {\n this._isExecuting = true\n this._lastResult = await this.fn(...args) // EXECUTE!\n this._successCount++\n this._options.onSuccess?.(this._lastResult!, this)\n } catch (error) {\n this._errorCount++\n this._options.onError?.(error, this)\n if (this._options.throwOnError) {\n throw error\n }\n } finally {\n this._isExecuting = false\n this._isPending = false\n this._settleCount++\n this._abortController = null\n this._options.onSettled?.(this)\n }\n return this._lastResult\n }\n\n /**\n * Cancel without resetting _canLeadingExecute\n */\n private _cancel(): void {\n if (this._timeoutId) {\n clearTimeout(this._timeoutId)\n this._timeoutId = null\n }\n if (this._abortController) {\n this._abortController.abort()\n this._abortController = null\n }\n if (this._resolvePreviousPromise) {\n this._resolvePreviousPromise(this._lastResult)\n this._resolvePreviousPromise = null\n }\n this._lastArgs = undefined\n this._isPending = false\n this._isExecuting = false\n }\n\n /**\n * Cancels any pending execution or aborts any execution in progress\n */\n cancel(): void {\n this._canLeadingExecute = true\n this._cancel()\n }\n\n /**\n * Returns the last result of the debounced function\n */\n getLastResult(): ReturnType<TFn> | undefined {\n return this._lastResult\n }\n\n /**\n * Returns the number of times the function has been executed successfully\n */\n getSuccessCount(): number {\n return this._successCount\n }\n\n /**\n * Returns the number of times the function has settled (completed or errored)\n */\n getSettleCount(): number {\n return this._settleCount\n }\n\n /**\n * Returns the number of times the function has errored\n */\n getErrorCount(): number {\n return this._errorCount\n }\n\n /**\n * Returns `true` if there is a pending execution queued up for trailing execution\n */\n getIsPending(): boolean {\n return this.getEnabled() && this._isPending\n }\n\n /**\n * Returns `true` if there is currently an execution in progress\n */\n getIsExecuting(): boolean {\n return this._isExecuting\n }\n}\n\n/**\n * Creates an async debounced function that delays execution until after a specified wait time.\n * The debounced function will only execute once the wait period has elapsed without any new calls.\n * If called again during the wait period, the timer resets and a new wait period begins.\n *\n * Unlike the non-async Debouncer, this async version supports returning values from the debounced function,\n * making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call\n * instead of setting the result on a state variable from within the debounced function.\n *\n * Error Handling:\n * - If an `onError` handler is provided, it will be called with the error and debouncer instance\n * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown\n * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed\n * - The error state can be checked using the underlying AsyncDebouncer instance\n * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown\n *\n * @example\n * ```ts\n * const debounced = asyncDebounce(async (value: string) => {\n * const result = await saveToAPI(value);\n * return result; // Return value is preserved\n * }, {\n * wait: 1000,\n * onError: (error) => {\n * console.error('API call failed:', error);\n * },\n * throwOnError: true // Will both log the error and throw it\n * });\n *\n * // Will only execute once, 1 second after the last call\n * // Returns the API response directly\n * const result = await debounced(\"third\");\n * ```\n */\nexport function asyncDebounce<TFn extends AnyAsyncFunction>(\n fn: TFn,\n initialOptions: AsyncDebouncerOptions<TFn>,\n) {\n const asyncDebouncer = new AsyncDebouncer(fn, initialOptions)\n return asyncDebouncer.maybeExecute.bind(asyncDebouncer)\n}\n"],"names":["parseFunctionOrValue"],"mappings":"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"}
@@ -96,10 +96,10 @@ export declare class AsyncDebouncer<TFn extends AnyAsyncFunction> {
96
96
  private _settleCount;
97
97
  private _successCount;
98
98
  private _timeoutId;
99
+ private _resolvePreviousPromise;
99
100
  constructor(fn: TFn, initialOptions: AsyncDebouncerOptions<TFn>);
100
101
  /**
101
102
  * Updates the debouncer options
102
- * Returns the new options state
103
103
  */
104
104
  setOptions(newOptions: Partial<AsyncDebouncerOptions<TFn>>): void;
105
105
  /**
@@ -129,7 +129,7 @@ export declare class AsyncDebouncer<TFn extends AnyAsyncFunction> {
129
129
  * @throws The error from the debounced function if no onError handler is configured
130
130
  */
131
131
  maybeExecute(...args: Parameters<TFn>): Promise<ReturnType<TFn> | undefined>;
132
- private executeFunction;
132
+ private execute;
133
133
  /**
134
134
  * Cancel without resetting _canLeadingExecute
135
135
  */
@@ -14,7 +14,8 @@ const defaultOptions = {
14
14
  wait: 0
15
15
  };
16
16
  class AsyncQueuer {
17
- constructor(initialOptions = defaultOptions) {
17
+ constructor(fn, initialOptions) {
18
+ this.fn = fn;
18
19
  this._activeItems = /* @__PURE__ */ new Set();
19
20
  this._successCount = 0;
20
21
  this._errorCount = 0;
@@ -37,32 +38,33 @@ class AsyncQueuer {
37
38
  }
38
39
  }
39
40
  /**
40
- * Updates the queuer options
41
- * Returns the new options state
41
+ * Updates the queuer options. New options are merged with existing options.
42
42
  */
43
43
  setOptions(newOptions) {
44
44
  this._options = { ...this._options, ...newOptions };
45
45
  }
46
46
  /**
47
- * Returns the current queuer options
47
+ * Returns the current queuer options, including defaults and any overrides.
48
48
  */
49
49
  getOptions() {
50
50
  return this._options;
51
51
  }
52
52
  /**
53
- * Returns the current wait time between processing items
53
+ * Returns the current wait time (in milliseconds) between processing items.
54
+ * If a function is provided, it is called with the queuer instance.
54
55
  */
55
56
  getWait() {
56
57
  return utils.parseFunctionOrValue(this._options.wait, this);
57
58
  }
58
59
  /**
59
- * Returns the current concurrency limit
60
+ * Returns the current concurrency limit for processing items.
61
+ * If a function is provided, it is called with the queuer instance.
60
62
  */
61
63
  getConcurrency() {
62
64
  return utils.parseFunctionOrValue(this._options.concurrency, this);
63
65
  }
64
66
  /**
65
- * Processes items in the queuer
67
+ * Processes items in the queue up to the concurrency limit. Internal use only.
66
68
  */
67
69
  tick() {
68
70
  var _a, _b;
@@ -72,33 +74,14 @@ class AsyncQueuer {
72
74
  }
73
75
  this.checkExpiredItems();
74
76
  while (this._activeItems.size < this.getConcurrency() && !this.getIsEmpty()) {
75
- const nextFn = this.getNextItem();
76
- if (!nextFn) {
77
+ const nextItem = this.getPeek();
78
+ if (!nextItem) {
77
79
  break;
78
80
  }
79
- this._activeItems.add(nextFn);
81
+ this._activeItems.add(nextItem);
80
82
  (_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
81
83
  (async () => {
82
- var _a2, _b2, _c, _d, _e, _f, _g, _h;
83
- let res;
84
- try {
85
- res = await nextFn();
86
- this._successCount++;
87
- (_b2 = (_a2 = this._options).onSuccess) == null ? void 0 : _b2.call(_a2, res, this);
88
- } catch (error) {
89
- this._errorCount++;
90
- (_d = (_c = this._options).onError) == null ? void 0 : _d.call(_c, error, this);
91
- if (this._options.throwOnError) {
92
- throw error;
93
- } else {
94
- console.error(error);
95
- }
96
- } finally {
97
- this._settledCount++;
98
- this._activeItems.delete(nextFn);
99
- (_f = (_e = this._options).onItemsChange) == null ? void 0 : _f.call(_e, this);
100
- (_h = (_g = this._options).onSettled) == null ? void 0 : _h.call(_g, this);
101
- }
84
+ this._lastResult = await this.execute();
102
85
  const wait = this.getWait();
103
86
  if (wait > 0) {
104
87
  setTimeout(() => this.tick(), wait);
@@ -110,40 +93,7 @@ class AsyncQueuer {
110
93
  this._pendingTick = false;
111
94
  }
112
95
  /**
113
- * Checks for and removes expired items from the queuer
114
- */
115
- checkExpiredItems() {
116
- var _a, _b, _c, _d;
117
- if (this._options.expirationDuration === Infinity && this._options.getIsExpired === defaultOptions.getIsExpired)
118
- return;
119
- const now = Date.now();
120
- const expiredIndices = [];
121
- for (let i = 0; i < this._items.length; i++) {
122
- const timestamp = this._itemTimestamps[i];
123
- if (timestamp === void 0) continue;
124
- const item = this._items[i];
125
- if (item === void 0) continue;
126
- const isExpired = this._options.getIsExpired !== defaultOptions.getIsExpired ? this._options.getIsExpired(item, timestamp) : now - timestamp > this._options.expirationDuration;
127
- if (isExpired) {
128
- expiredIndices.push(i);
129
- }
130
- }
131
- for (let i = expiredIndices.length - 1; i >= 0; i--) {
132
- const index = expiredIndices[i];
133
- if (index === void 0) continue;
134
- const expiredItem = this._items[index];
135
- if (expiredItem === void 0) continue;
136
- this._items.splice(index, 1);
137
- this._itemTimestamps.splice(index, 1);
138
- this._expirationCount++;
139
- (_b = (_a = this._options).onExpire) == null ? void 0 : _b.call(_a, expiredItem, this);
140
- }
141
- if (expiredIndices.length > 0) {
142
- (_d = (_c = this._options).onItemsChange) == null ? void 0 : _d.call(_c, this);
143
- }
144
- }
145
- /**
146
- * Starts the queuer and processes items
96
+ * Starts processing items in the queue. If already running, does nothing.
147
97
  */
148
98
  start() {
149
99
  var _a, _b;
@@ -153,19 +103,9 @@ class AsyncQueuer {
153
103
  this.tick();
154
104
  }
155
105
  (_b = (_a = this._options).onIsRunningChange) == null ? void 0 : _b.call(_a, this);
156
- return new Promise((resolve) => {
157
- const checkIdle = () => {
158
- if (this.getIsIdle()) {
159
- resolve();
160
- } else {
161
- setTimeout(checkIdle, 100);
162
- }
163
- };
164
- checkIdle();
165
- });
166
106
  }
167
107
  /**
168
- * Stops the queuer from processing items
108
+ * Stops processing items in the queue. Does not clear the queue.
169
109
  */
170
110
  stop() {
171
111
  var _a, _b;
@@ -174,7 +114,7 @@ class AsyncQueuer {
174
114
  (_b = (_a = this._options).onIsRunningChange) == null ? void 0 : _b.call(_a, this);
175
115
  }
176
116
  /**
177
- * Removes all items from the queuer
117
+ * Removes all pending items from the queue. Does not affect active tasks.
178
118
  */
179
119
  clear() {
180
120
  var _a, _b;
@@ -182,7 +122,8 @@ class AsyncQueuer {
182
122
  (_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
183
123
  }
184
124
  /**
185
- * Resets the queuer to its initial state
125
+ * Resets the queuer to its initial state. Optionally repopulates with initial items.
126
+ * Does not affect callbacks or options.
186
127
  */
187
128
  reset(withInitialItems) {
188
129
  this.clear();
@@ -195,34 +136,41 @@ class AsyncQueuer {
195
136
  this._running = this._options.started;
196
137
  }
197
138
  /**
198
- * Adds a task to the queuer
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
+ * ```
199
147
  */
200
- addItem(fn, position = this._options.addItemsTo, runOnItemsChange = true) {
148
+ addItem(item, position = this._options.addItemsTo, runOnItemsChange = true) {
201
149
  var _a, _b, _c, _d;
202
150
  if (this.getIsFull()) {
203
151
  this._rejectionCount++;
204
- (_b = (_a = this._options).onReject) == null ? void 0 : _b.call(_a, fn, this);
152
+ (_b = (_a = this._options).onReject) == null ? void 0 : _b.call(_a, item, this);
205
153
  return;
206
154
  }
207
- const priority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(fn) : fn.priority;
155
+ const priority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(item) : item.priority;
208
156
  if (priority !== void 0) {
209
157
  const insertIndex = this._items.findIndex((existing) => {
210
158
  const existingPriority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(existing) : existing.priority;
211
- return existingPriority > priority;
159
+ return existingPriority < priority;
212
160
  });
213
161
  if (insertIndex === -1) {
214
- this._items.push(fn);
162
+ this._items.push(item);
215
163
  this._itemTimestamps.push(Date.now());
216
164
  } else {
217
- this._items.splice(insertIndex, 0, fn);
165
+ this._items.splice(insertIndex, 0, item);
218
166
  this._itemTimestamps.splice(insertIndex, 0, Date.now());
219
167
  }
220
168
  } else {
221
169
  if (position === "front") {
222
- this._items.unshift(fn);
170
+ this._items.unshift(item);
223
171
  this._itemTimestamps.unshift(Date.now());
224
172
  } else {
225
- this._items.push(fn);
173
+ this._items.push(item);
226
174
  this._itemTimestamps.push(Date.now());
227
175
  }
228
176
  }
@@ -235,10 +183,19 @@ class AsyncQueuer {
235
183
  }
236
184
  }
237
185
  /**
238
- * Removes and returns an item from the queuer
186
+ * Removes and returns the next item from the queue without executing the task function.
187
+ * Use for manual queue management. Normally, use execute() to process items.
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * // FIFO
192
+ * queuer.getNextItem();
193
+ * // LIFO
194
+ * queuer.getNextItem('back');
195
+ * ```
239
196
  */
240
197
  getNextItem(position = this._options.getItemsFrom) {
241
- var _a, _b, _c, _d;
198
+ var _a, _b;
242
199
  let item;
243
200
  if (position === "front") {
244
201
  item = this._items.shift();
@@ -249,12 +206,84 @@ class AsyncQueuer {
249
206
  }
250
207
  if (item !== void 0) {
251
208
  (_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
252
- (_d = (_c = this._options).onGetNextItem) == null ? void 0 : _d.call(_c, item, this);
253
209
  }
254
210
  return item;
255
211
  }
256
212
  /**
257
- * Returns an item without removing it
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;
235
+ }
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
+ }
242
+ }
243
+ return item;
244
+ }
245
+ /**
246
+ * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.
247
+ * Internal use only.
248
+ */
249
+ checkExpiredItems() {
250
+ var _a, _b, _c, _d;
251
+ if (this._options.expirationDuration === Infinity && this._options.getIsExpired === defaultOptions.getIsExpired)
252
+ return;
253
+ const now = Date.now();
254
+ const expiredIndices = [];
255
+ for (let i = 0; i < this._items.length; i++) {
256
+ const timestamp = this._itemTimestamps[i];
257
+ if (timestamp === void 0) continue;
258
+ const item = this._items[i];
259
+ if (item === void 0) continue;
260
+ const isExpired = this._options.getIsExpired !== defaultOptions.getIsExpired ? this._options.getIsExpired(item, timestamp) : now - timestamp > this._options.expirationDuration;
261
+ if (isExpired) {
262
+ expiredIndices.push(i);
263
+ }
264
+ }
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
+ * ```
258
287
  */
259
288
  getPeek(position = "front") {
260
289
  if (position === "front") {
@@ -263,87 +292,87 @@ class AsyncQueuer {
263
292
  return this._items[this._items.length - 1];
264
293
  }
265
294
  /**
266
- * Returns true if the queuer is empty
295
+ * Returns true if the queue is empty (no pending items).
267
296
  */
268
297
  getIsEmpty() {
269
298
  return this._items.length === 0;
270
299
  }
271
300
  /**
272
- * Returns true if the queuer is full
301
+ * Returns true if the queue is full (reached maxSize).
273
302
  */
274
303
  getIsFull() {
275
304
  return this._items.length >= this._options.maxSize;
276
305
  }
277
306
  /**
278
- * Returns the current size of the queuer
307
+ * Returns the number of pending items in the queue.
279
308
  */
280
309
  getSize() {
281
310
  return this._items.length;
282
311
  }
283
312
  /**
284
- * Returns a copy of all items in the queuer
313
+ * Returns a copy of all items in the queue, including active and pending items.
285
314
  */
286
315
  getAllItems() {
287
316
  return [...this.getActiveItems(), ...this.getPendingItems()];
288
317
  }
289
318
  /**
290
- * Returns the active items
319
+ * Returns the items currently being processed (active tasks).
291
320
  */
292
321
  getActiveItems() {
293
322
  return Array.from(this._activeItems);
294
323
  }
295
324
  /**
296
- * Returns the pending items
325
+ * Returns the items waiting to be processed (pending tasks).
297
326
  */
298
327
  getPendingItems() {
299
328
  return [...this._items];
300
329
  }
301
330
  /**
302
- * Returns the number of items that have been successfully processed
331
+ * Returns the number of items that have been successfully processed.
303
332
  */
304
333
  getSuccessCount() {
305
334
  return this._successCount;
306
335
  }
307
336
  /**
308
- * Returns the number of items that have failed processing
337
+ * Returns the number of items that have failed processing.
309
338
  */
310
339
  getErrorCount() {
311
340
  return this._errorCount;
312
341
  }
313
342
  /**
314
- * Returns the number of items that have completed processing (success or error)
343
+ * Returns the number of items that have completed processing (success or error).
315
344
  */
316
345
  getSettledCount() {
317
346
  return this._settledCount;
318
347
  }
319
348
  /**
320
- * Returns the number of items that have been rejected from the queuer
349
+ * Returns the number of items that have been rejected from being added to the queue.
321
350
  */
322
351
  getRejectionCount() {
323
352
  return this._rejectionCount;
324
353
  }
325
354
  /**
326
- * Returns true if the queuer is running
355
+ * Returns true if the queuer is currently running (processing items).
327
356
  */
328
357
  getIsRunning() {
329
358
  return this._running;
330
359
  }
331
360
  /**
332
- * Returns true if the queuer is running but has no items to process
361
+ * Returns true if the queuer is running but has no items to process and no active tasks.
333
362
  */
334
363
  getIsIdle() {
335
364
  return this._running && this.getIsEmpty() && this._activeItems.size === 0;
336
365
  }
337
366
  /**
338
- * Returns the number of items that have expired from the queuer
367
+ * Returns the number of items that have expired and been removed from the queue.
339
368
  */
340
369
  getExpirationCount() {
341
370
  return this._expirationCount;
342
371
  }
343
372
  }
344
- function asyncQueue(options) {
345
- const queuer = new AsyncQueuer(options);
346
- return queuer.addItem.bind(queuer);
373
+ function asyncQueue(fn, initialOptions) {
374
+ const asyncQueuer = new AsyncQueuer(fn, initialOptions);
375
+ return asyncQueuer.addItem.bind(asyncQueuer);
347
376
  }
348
377
  exports.AsyncQueuer = AsyncQueuer;
349
378
  exports.asyncQueue = asyncQueue;