@superutils/promise 1.0.5 → 1.0.7

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  An extended `Promise` implementation, named `PromisE`, that provides additional features and utilities for easier asynchronous flow control in JavaScript and TypeScript applications.
4
4
 
5
- This package offers a drop-in replacement for the native `Promise` that includes status tracking (`.pending`, `.resolved`, `.rejected`) and a suite of powerful static methods for common asynchronous patterns like deferred execution, throttling, and cancellable fetches.
5
+ This package offers a drop-in replacement for the native `Promise` that includes status tracking (`.pending`, `.resolved`, `.rejected`) and a suite of practical static methods for common asynchronous patterns like deferred execution, throttling, and cancellable fetches.
6
6
 
7
7
  <div v-if="false">
8
8
 
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ interface IPromisE<T = unknown> extends Promise<T> {
17
17
  /** Indicates if the promise has been resolved */
18
18
  readonly resolved: boolean;
19
19
  }
20
- interface IPromisE_Delay<T = unknown> extends IPromisE<T> {
20
+ interface IPromisE_Delay<T = unknown> extends Promise<T>, IPromisE<T> {
21
21
  /**
22
22
  * Caution: pausing will prevent the promise from resolving/rejeting automatically.
23
23
  *
@@ -58,6 +58,7 @@ interface IPromisE_Delay<T = unknown> extends IPromisE<T> {
58
58
  * ```
59
59
  */
60
60
  pause: () => void;
61
+ /** Timeout ID */
61
62
  timeoutId: TimeoutId;
62
63
  }
63
64
  /**
@@ -184,13 +185,13 @@ type RetryOptions<T = unknown> = {
184
185
  * Additional condition/function to be used to determine whether function should be retried.
185
186
  * `retryIf` will only be executed when function execution is successful.
186
187
  */
187
- retryIf?: null | ((prevResult: T | undefined, retryCount: number, error?: unknown) => boolean | Promise<boolean>);
188
+ retryIf?: null | ((prevResult: T | undefined, retryCount: number) => boolean | Promise<boolean>);
188
189
  };
189
190
 
190
191
  declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T> {
191
- readonly state: 0 | 1 | 2;
192
192
  private _resolve?;
193
193
  private _reject?;
194
+ private _state;
194
195
  /**
195
196
  * callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve()/reject() methods */
196
197
  onEarlyFinalize: OnEarlyFinalize<T>[];
@@ -219,6 +220,8 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
219
220
  get rejected(): boolean;
220
221
  /** Indicates if the promise has been resolved */
221
222
  get resolved(): boolean;
223
+ /** Get promise status code */
224
+ get state(): 0 | 1 | 2;
222
225
  /** Resovle pending promise early. */
223
226
  resolve: (value: T | PromiseLike<T>) => void;
224
227
  /** Reject pending promise early. */
@@ -293,7 +296,38 @@ type TimeoutOptions<Func extends string = 'all'> = {
293
296
  * @function PromisE.deferred
294
297
  * The adaptation of the `deferred()` function tailored for Promises.
295
298
  *
296
- * @param options (optional) options
299
+ *
300
+ * # Notes
301
+ *
302
+ * - A "request" simply means invokation of the returned callback function
303
+ * - By "handled" it means a "request" will be resolved or rejected.
304
+ * - `PromisE.deferred` is to be used with promises/functions.
305
+ * `PromisE.deferredCallback` is for use with callback functions.
306
+ * - There is no specific time delay.
307
+ * - If a request takes longer than `delayMs`, the following request will be added to queue
308
+ * and either be ignored or exectued based on the debounce/throttle configuration.
309
+ * - If not throttled:
310
+ * 1. Once a request is handled, all previous requests will be ignored and pool starts anew.
311
+ * 2. If a function is provided in the returned callback, ALL of them will be invoked, regardless of pool size.
312
+ * 3. The last/only request in an on-going requests' pool will handled (resolve/reject).
313
+ * - If throttled:
314
+ * 1. Once a requst starts executing, subsequent requests will be added to a queue.
315
+ * 2. The last/only item in the queue will be handled. Rest will be ignored.
316
+ * 3. If a function is provided in the returned callback, it will be invoked only if the request is handled.
317
+ * Thus, improving performance by avoiding unnecessary invokations.
318
+ * 4. If every single request/function needs to be invoked, avoid using throttle.
319
+ * - If throttled and `strict` is truthy, all subsequent request while a request is being handled will be ignored.
320
+ *
321
+ * @param options (optional) Debounce/throttle configuration.
322
+ *
323
+ * The properties' default values can be overridden to be EFFECTIVE GLOBALLY:
324
+ * ```typescript
325
+ * deferred.defaults = {
326
+ * delayMs: 100,
327
+ * resolveError: ResolveError.REJECT,
328
+ * resolveIgnored: ResolveIgnored.WITH_LAST,
329
+ * }
330
+ * ```
297
331
  * @property options.delayMs (optional) delay in milliseconds to be used with debounce & throttle modes. When `undefined` or `>= 0`, execution will be sequential.
298
332
  * @property options.onError (optional)
299
333
  * @property options.onIgnore (optional) invoked whenever callback invocation is ignored by a newer invocation
@@ -305,30 +339,11 @@ type TimeoutOptions<Func extends string = 'all'> = {
305
339
  * Requires `defer`.
306
340
  * Default: `false`
307
341
  *
308
- * @returns {Function} a callback that is invoked in one of the followin 3 methods:
309
- * - sequential: when `delayMs <= 0` or `delayMs = undefined`
342
+ * @returns Callback function that can be invoked in one of the followin 3 methods:
343
+ * - sequential: when `delayMs <= 0`
310
344
  * - debounced: when `delayMs > 0` and `throttle = false`
311
345
  * - throttled: when `delayMs > 0` and `throttle = true`
312
346
  *
313
- * The main difference is that:
314
- * - Notes:
315
- * 1. A "request" simply means invokation of the returned callback function
316
- * 2. By "handled" it means a "request" will be resolved or rejected.
317
- * - `PromisE.deferred` is to be used with promises/functions
318
- * - There is no specific time delay.
319
- * - The time when a request is completed is irrelevant.
320
- * - If not throttled:
321
- * 1. Once a request is handled, all previous requests will be ignored and pool starts anew.
322
- * 2. If a function is provided in the returned callback, ALL of them will be invoked, regardless of pool size.
323
- * 3. The last/only request in an on-going requests' pool will handled (resolve/reject).
324
- * - If throttled:
325
- * 1. Once a requst starts executing, subsequent requests will be added to a queue.
326
- * 2. The last/only item in the queue will be handled. Rest will be ignored.
327
- * 3. If a function is provided in the returned callback, it will be invoked only if the request is handled.
328
- * Thus, improving performance by avoiding unnecessary invokations.
329
- * 4. If every single request/function needs to be invoked, avoid using throttle.
330
- *
331
- * - If throttled and `strict` is truthy, all subsequent request while a request is being handled will be ignored.
332
347
  *
333
348
  * @example Explanation & example usage:
334
349
  * ```typescript
@@ -400,15 +415,19 @@ declare namespace deferred {
400
415
  declare function deferredCallback<TDefault, ThisArg, Delay extends number = number, CbArgs extends unknown[] = unknown[]>(callback: (...args: CbArgs) => TDefault | Promise<TDefault>, options?: DeferredAsyncOptions<ThisArg, Delay>): <TResult = TDefault>(...args: CbArgs) => IPromisE<TResult>;
401
416
 
402
417
  /**
403
- * @function PromisE.delay
404
- * @summary Creates a promise that completes after given delay/duration.
418
+ * Creates a promise that completes after given delay/duration.
419
+ *
420
+ * Also accessible from the `PromisE` class as `PromisE.delay()`.
405
421
  *
406
- * @param {Number} duration duration in milliseconds
407
- * @param {unknown} result (optional) specify a value to resolve or reject with.
408
- * Default: `delayMs` when resolved or timed out error when rejected
409
- * @param {boolean} asRejected (optional) if `true`, will reject the promise after the delay.
422
+ * @param duration duration in milliseconds. Default: `100`
423
+ * @param result (optional) specify a value to resolve or error to reject with.
410
424
  *
411
- * @returns See {@link IPromisE_Delay}
425
+ * Alternatively, a function (with no arguments) can be provided that returns the result.
426
+ *
427
+ * Default: `delayMs` when resolved or timed out error when rejected
428
+ * @param asRejected (optional) if `true`, will reject the promise after the delay.
429
+ *
430
+ * @returns a promise
412
431
  *
413
432
  * @example Delay before continuing execution
414
433
  * ```typescript
@@ -433,7 +452,7 @@ declare namespace delay {
433
452
  var defaults: {
434
453
  /** Default delay duration in milliseconds */
435
454
  duration: number;
436
- /** Default timed out message */
455
+ /** Default timed out message (if `result` is not provided) */
437
456
  delayTimeoutMsg: string;
438
457
  };
439
458
  }
@@ -630,7 +649,7 @@ declare class PromisE<T = unknown> extends PromisEBase<T> {
630
649
  }
631
650
 
632
651
  /**
633
- * Executes a function and retries it on failure or until a specific condition is met.
652
+ * Executes a function asynchronously and retries it on failure or until a specific condition is met.
634
653
  *
635
654
  * The function will be re-executed if:
636
655
  * 1. The `func` promise rejects or the function throws an error.
@@ -640,21 +659,56 @@ declare class PromisE<T = unknown> extends PromisEBase<T> {
640
659
  * Retries will stop when the `retry` count is exhausted, or when `func` executes successfully
641
660
  * (resolves without error) AND the `retryIf` (if provided) returns `false`.
642
661
  *
643
- * @template T The type of the value that the `func` returns/resolves to.
644
- * @param {() => ValueOrPromise<T>} func The function to execute. It can be synchronous or asynchronous.
645
- * @param {RetryOptions} [options={}] (optional) Options for configuring the retry mechanism.
646
- * @property {number} [options.retry=1] (optional) The maximum number of retries.
647
- * @property {number} [options.retryDelayMs=300] The base delay in milliseconds between retries.
648
- * @property {'exponential' | 'fixed'} [options.retryBackOff='exponential'] The backoff strategy. 'exponential' doubles the delay for each subsequent retry. 'fixed' uses a constant delay.
649
- * @property {boolean} [options.retryDelayJitter=true] If true, adds a random jitter to the delay to prevent thundering herd problem.
650
- * @property {number} [options.retryDelayJitterMax=100] The maximum jitter in milliseconds to add to the delay.
651
- * @property {(result: T | undefined, retryCount: number) => boolean} [options.retryIf] A function that is called after a successful execution of `func`. If it returns `true`, a retry is triggered. It receives the result and the current retry count.
652
- * @returns {Promise<T | undefined>} A promise that resolves with the result of the last successful execution of `func`.
653
- * If all retries fail (either by throwing an error or by the condition function always returning true),
654
- * it resolves with `undefined`. Errors thrown by `func` are caught and handled internally, not re-thrown.
662
+ * @template T The type of the value that the `func` resolves to.
663
+ *
664
+ * @param func The function to execute. It can be synchronous or asynchronous.
665
+ * @param options (optional) Configuration of the retry mechanism.
666
+ *
667
+ * The following options' default values can be configured to be EFFECTIVE GLOBALLY.
668
+ *
669
+ * ```typescript
670
+ * PromisE.retry.defaults = {
671
+ * retry: 1,
672
+ * retryBackOff: 'exponential',
673
+ * retryDelay: 300,
674
+ * retryDelayJitter: true,
675
+ * retryDelayJitterMax: 100,
676
+ * }
677
+ * ```
678
+ * @param options.retry (optional) The maximum number of retries. Default: `1`
679
+ * @param options.retryBackOff (optional) The backoff strategy.
680
+ * Accepted values:
681
+ * - `'exponential'` doubles the delay for each subsequent retry.
682
+ * - `'linear'` uses a constant delay.
683
+ *
684
+ * Default: `'exponential'`
685
+ * @param options.retryDelayMs (optional) The base delay in milliseconds between retries.
686
+ *
687
+ * Default: `300`
688
+ * @param options.retryDelayJitter (optional) If true, adds a random jitter to the delay to prevent
689
+ * the thundering herd problem.
690
+ *
691
+ * Default: `true`
692
+ * @param options.retryDelayJitterMax The maximum jitter in milliseconds to add to the delay.
693
+ *
694
+ * Default: `100`
695
+ * @param options.retryIf (optional) A function that is called after a successful execution of `func`.
696
+ * If it returns `true`, a retry is triggered.
697
+ *
698
+ * **Arguments:**
699
+ * - `result`: result received after the most recent `func` excution
700
+ * - `retryCount`: number of times the execution has been retried (`total_attemts - 1`)
701
+ *
702
+ * If `retryIf()` throws error, the are handled gracefully and it's return value defaults `false` (no further retry).
703
+ *
704
+ * @returns A promise that resolves with the result of the last successful execution of `func`.
705
+ * If all retries fail (either by throwing an error or when `retryIf()` returned true in every time),
706
+ * it resolves with the last return value of `func()`. Errors thrown by `func` are caught and handled internally,
707
+ * only re-thrown if no result is received after maximum retries.
655
708
  */
656
709
  declare const retry: {
657
710
  <T>(func: () => ValueOrPromise<T>, options: RetryOptions<T>): Promise<T>;
711
+ /** Global default values */
658
712
  defaults: {
659
713
  retry: number;
660
714
  retryBackOff: "exponential";
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  } from "@superutils/core";
10
10
 
11
11
  // src/PromisEBase.ts
12
- import { asAny, fallbackIfFails, isFn, isPromise } from "@superutils/core";
12
+ import { fallbackIfFails, isFn, isPromise } from "@superutils/core";
13
13
  var _PromisEBase = class _PromisEBase extends Promise {
14
14
  constructor(input) {
15
15
  if (input instanceof _PromisEBase) return input;
@@ -17,11 +17,11 @@ var _PromisEBase = class _PromisEBase extends Promise {
17
17
  let _reject;
18
18
  super((resolve, reject) => {
19
19
  _reject = (reason) => {
20
- asAny(this).state = 2;
20
+ this._state = 2;
21
21
  reject(reason);
22
22
  };
23
23
  _resolve = (value) => {
24
- asAny(this).state = 1;
24
+ this._state = 1;
25
25
  resolve(value);
26
26
  };
27
27
  input != null ? input : input = () => {
@@ -29,7 +29,7 @@ var _PromisEBase = class _PromisEBase extends Promise {
29
29
  const promise = isPromise(input) ? input : isFn(input) ? new globalThis.Promise(input) : Promise.resolve(input);
30
30
  promise.then(_resolve, _reject);
31
31
  });
32
- this.state = 0;
32
+ this._state = 0;
33
33
  /**
34
34
  * callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve()/reject() methods */
35
35
  this.onEarlyFinalize = [];
@@ -66,15 +66,19 @@ var _PromisEBase = class _PromisEBase extends Promise {
66
66
  //
67
67
  /** Indicates if the promise is still pending/unfinalized */
68
68
  get pending() {
69
- return this.state === 0;
69
+ return this._state === 0;
70
70
  }
71
71
  /** Indicates if the promise has been rejected */
72
72
  get rejected() {
73
- return this.state === 2;
73
+ return this._state === 2;
74
74
  }
75
75
  /** Indicates if the promise has been resolved */
76
76
  get resolved() {
77
- return this.state === 1;
77
+ return this._state === 1;
78
+ }
79
+ /** Get promise status code */
80
+ get state() {
81
+ return this._state;
78
82
  }
79
83
  // static withResolvers = <T = unknown>() => {
80
84
  // const pwr = globalThis.Promise.withResolvers<T>()
@@ -170,7 +174,6 @@ function deferred(options) {
170
174
  options = objCopy(defaults, options, [], "empty");
171
175
  let { onError, onIgnore, onResult } = options;
172
176
  const {
173
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
174
177
  delayMs,
175
178
  resolveError,
176
179
  // by default reject on error
@@ -259,7 +262,7 @@ function deferred(options) {
259
262
  return deferredFunc;
260
263
  }
261
264
  deferred.defaults = {
262
- delayMs: -100,
265
+ delayMs: 100,
263
266
  resolveError: "REJECT" /* REJECT */,
264
267
  resolveIgnored: "WITH_LAST" /* WITH_LAST */
265
268
  };
@@ -284,7 +287,9 @@ function delay(duration = delay.defaults.duration, result = duration, asRejected
284
287
  result2 = (_a = fallbackIfFails3(result2, [], void 0)) != null ? _a : duration;
285
288
  if (!asRejected) return promise.resolve(result2);
286
289
  promise.reject(
287
- result2 != null ? result2 : new Error(`${delay.defaults.delayTimeoutMsg} ${duration}ms`)
290
+ duration !== result2 && result2 !== void 0 ? result2 : new Error(
291
+ `${delay.defaults.delayTimeoutMsg} ${duration}ms`
292
+ )
288
293
  );
289
294
  };
290
295
  promise.timeoutId = setTimeout(() => finalize(result), duration);
@@ -296,7 +301,7 @@ function delay(duration = delay.defaults.duration, result = duration, asRejected
296
301
  delay.defaults = {
297
302
  /** Default delay duration in milliseconds */
298
303
  duration: 100,
299
- /** Default timed out message */
304
+ /** Default timed out message (if `result` is not provided) */
300
305
  delayTimeoutMsg: "Timed out after"
301
306
  };
302
307
  var delay_default = delay;
@@ -353,7 +358,7 @@ var retry = async (func, options) => {
353
358
  }
354
359
  shouldRetry = maxRetries > 0 && retryCount < maxRetries && (!!error || !!await fallbackIfFails4(
355
360
  options.retryIf,
356
- [result, retryCount, error],
361
+ [result, retryCount],
357
362
  false
358
363
  ));
359
364
  } while (shouldRetry);
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/alien45/superutils/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@superutils/core": "^1.0.5"
7
+ "@superutils/core": "^1.0.7"
8
8
  },
9
9
  "description": "An extended Promise with extra features such as status tracking, deferred/throttled execution, timeout and retry mechanism.",
10
10
  "files": [
@@ -23,7 +23,7 @@
23
23
  "main": "dist/index.js",
24
24
  "name": "@superutils/promise",
25
25
  "peerDpendencies": {
26
- "@superutils/core": "^1.0.5"
26
+ "@superutils/core": "^1.0.7"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
@@ -43,5 +43,5 @@
43
43
  "sideEffects": false,
44
44
  "type": "module",
45
45
  "types": "dist/index.d.ts",
46
- "version": "1.0.5"
46
+ "version": "1.0.7"
47
47
  }