@superutils/promise 1.0.3 → 1.0.6

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 (3) hide show
  1. package/dist/index.d.ts +215 -111
  2. package/dist/index.js +80 -67
  3. package/package.json +5 -4
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _superutils_core from '@superutils/core';
2
- import { ValueOrPromise, TimeoutId, ThrottleConfig, DeferredConfig } from '@superutils/core';
2
+ import { ValueOrPromise, TimeoutId, PositiveNumber, ThrottleOptions, DeferredOptions } from '@superutils/core';
3
3
 
4
4
  interface IPromisE<T = unknown> extends Promise<T> {
5
5
  /** 0: pending, 1: resolved, 2: rejected */
@@ -77,25 +77,32 @@ type OnEarlyFinalize<T> = <TResolved extends boolean, TValue = TResolved extends
77
77
  type PromiseParams<T = unknown> = ConstructorParameters<typeof Promise<T>>;
78
78
 
79
79
  /** Return type of `PromisE.deferred()` */
80
- type DeferredReturn<TArgs extends unknown[] | [] = []> = <TResult = unknown>(promise: Promise<TResult> | ((...args: TArgs) => Promise<TResult>)) => IPromisE<TResult>;
81
- type DeferredOptions<ThisArg = unknown> = {
82
- /** Delay in milliseconds, used for `debounce` and `throttle` modes. */
83
- delayMs?: number;
80
+ type DeferredAsyncCallback<TArgs extends unknown[] | [] = []> = <TResult = unknown>(promise: Promise<TResult> | ((...args: TArgs) => Promise<TResult>)) => IPromisE<TResult>;
81
+ type DeferredAsyncGetPromise<T> = <TResult = T>() => Promise<TResult>;
82
+ /** Default options used by `PromisE.deferred` and related functions */
83
+ type DeferredAsyncDefaults = Pick<Required<DeferredAsyncOptions>, 'delayMs' | 'resolveError' | 'resolveIgnored'>;
84
+ /** Options for `PromisE.deferred` and other related functions */
85
+ type DeferredAsyncOptions<ThisArg = unknown, DelayMs extends number = number> = {
86
+ /**
87
+ * Delay in milliseconds, used for `debounce` and `throttle` modes.
88
+ *
89
+ * Default: `100`
90
+ */
84
91
  /** Callback invoked whenever promise/function throws error */
85
- onError?: (err: unknown) => ValueOrPromise<unknown>;
92
+ onError?: (this: ThisArg, err: unknown) => ValueOrPromise<unknown>;
86
93
  /**
87
94
  * Whenever a promise/function is ignored when in debource/throttle mode, `onIgnored` wil be invoked.
88
95
  * The promise/function will not be invoked, unless it's manually invoked using the `ignored` function.
89
96
  * Use for debugging or logging purposes.
90
97
  */
91
- onIgnore?: (ignored: () => Promise<unknown>) => ValueOrPromise<unknown>;
98
+ onIgnore?: (this: ThisArg, ignored: DeferredAsyncGetPromise<unknown>) => ValueOrPromise<unknown>;
92
99
  /**
93
100
  * Whenever a promise/function is executed successfully `onResult` will be called.
94
101
  * Those that are ignored but resolve with last will not cause `onResult` to be invoked.
95
102
  *
96
103
  * Result can be `undefined` if `ResolveIgnored.WITH_UNDEFINED` is used.
97
104
  */
98
- onResult?: (result?: unknown) => ValueOrPromise<unknown>;
105
+ onResult?: (this: ThisArg, result?: any) => ValueOrPromise<unknown>;
99
106
  /**
100
107
  * Indicates what to do when a promise in the queue is ignored.
101
108
  * See {@link ResolveIgnored} for available options.
@@ -106,16 +113,18 @@ type DeferredOptions<ThisArg = unknown> = {
106
113
  * See {@link ResolveError} for available options.
107
114
  */
108
115
  resolveError?: ResolveError;
109
- /** Enable throttle mode. Requires {@link DeferredOptions.delayMs}*/
110
- throttle?: boolean;
116
+ /** The value to be used as "thisArg" whenever any of the callbacks are invoked */
117
+ thisArg?: ThisArg;
111
118
  } & (({
112
- delayMs: number;
119
+ /** Throttle duration in milliseconds */
120
+ delayMs?: PositiveNumber<DelayMs>;
113
121
  throttle: true;
114
- } & ThrottleConfig<ThisArg>) | ({
115
- delayMs?: number;
116
- throttle?: false;
117
- } & DeferredConfig<ThisArg>));
118
- /** Options for what to do when deferred promise/function fails */
122
+ } & Omit<ThrottleOptions, 'onError' | 'ThisArg' | 'tid'>) | ({
123
+ /** Debounce/deferred duration in milliseconds */
124
+ delayMs?: PositiveNumber<DelayMs>;
125
+ throttle?: false | undefined;
126
+ } & Omit<DeferredOptions, 'onError' | 'ThisArg' | 'tid'>));
127
+ /** Determines what to do when deferred promise/function fails */
119
128
  declare enum ResolveError {
120
129
  /** Neither resolve nor reject the failed */
121
130
  NEVER = "NEVER",
@@ -126,7 +135,10 @@ declare enum ResolveError {
126
135
  /** Resolve with undefined */
127
136
  WITH_UNDEFINED = "RESOLVE_UNDEFINED"
128
137
  }
129
- /** Options for what to do when a promise/callback is ignored, either because of being deferred, throttled or another been prioritized. */
138
+ /**
139
+ * Determines what to do when a promise/callback is ignored, either because of being
140
+ * deferred, throttled or another been prioritized.
141
+ */
130
142
  declare enum ResolveIgnored {
131
143
  /** Never resolve ignored promises. Caution: make sure this doesn't cause any memory leaks. */
132
144
  NEVER = "NEVER",
@@ -136,22 +148,6 @@ declare enum ResolveIgnored {
136
148
  WITH_UNDEFINED = "WITH_UNDEFINED"
137
149
  }
138
150
 
139
- /** Global configuration */
140
- declare const config: {
141
- /** Default value for `options` used by `PromisE.*deferred*` functions */
142
- deferOptions: DeferredOptions;
143
- delayTimeoutMsg: string;
144
- retryOptions: {
145
- retry: number;
146
- retryBackOff: "exponential";
147
- retryDelay: number;
148
- retryDelayJitter: true;
149
- retryDelayJitterMax: number;
150
- retryIf: null;
151
- };
152
- };
153
- type Config = typeof config;
154
-
155
151
  /** Options for automatic retry mechanism */
156
152
  type RetryOptions<T = unknown> = {
157
153
  /**
@@ -188,7 +184,7 @@ type RetryOptions<T = unknown> = {
188
184
  * Additional condition/function to be used to determine whether function should be retried.
189
185
  * `retryIf` will only be executed when function execution is successful.
190
186
  */
191
- retryIf?: null | ((prevResult: T | undefined, retryCount: number, error?: unknown) => boolean);
187
+ retryIf?: null | ((prevResult: T | undefined, retryCount: number) => boolean | Promise<boolean>);
192
188
  };
193
189
 
194
190
  declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T> {
@@ -297,7 +293,38 @@ type TimeoutOptions<Func extends string = 'all'> = {
297
293
  * @function PromisE.deferred
298
294
  * The adaptation of the `deferred()` function tailored for Promises.
299
295
  *
300
- * @param options (optional) options
296
+ *
297
+ * # Notes
298
+ *
299
+ * - A "request" simply means invokation of the returned callback function
300
+ * - By "handled" it means a "request" will be resolved or rejected.
301
+ * - `PromisE.deferred` is to be used with promises/functions.
302
+ * `PromisE.deferredCallback` is for use with callback functions.
303
+ * - There is no specific time delay.
304
+ * - If a request takes longer than `delayMs`, the following request will be added to queue
305
+ * and either be ignored or exectued based on the debounce/throttle configuration.
306
+ * - If not throttled:
307
+ * 1. Once a request is handled, all previous requests will be ignored and pool starts anew.
308
+ * 2. If a function is provided in the returned callback, ALL of them will be invoked, regardless of pool size.
309
+ * 3. The last/only request in an on-going requests' pool will handled (resolve/reject).
310
+ * - If throttled:
311
+ * 1. Once a requst starts executing, subsequent requests will be added to a queue.
312
+ * 2. The last/only item in the queue will be handled. Rest will be ignored.
313
+ * 3. If a function is provided in the returned callback, it will be invoked only if the request is handled.
314
+ * Thus, improving performance by avoiding unnecessary invokations.
315
+ * 4. If every single request/function needs to be invoked, avoid using throttle.
316
+ * - If throttled and `strict` is truthy, all subsequent request while a request is being handled will be ignored.
317
+ *
318
+ * @param options (optional) Debounce/throttle configuration.
319
+ *
320
+ * The properties' default values can be overridden to be EFFECTIVE GLOBALLY:
321
+ * ```typescript
322
+ * deferred.defaults = {
323
+ * delayMs: 100,
324
+ * resolveError: ResolveError.REJECT,
325
+ * resolveIgnored: ResolveIgnored.WITH_LAST,
326
+ * }
327
+ * ```
301
328
  * @property options.delayMs (optional) delay in milliseconds to be used with debounce & throttle modes. When `undefined` or `>= 0`, execution will be sequential.
302
329
  * @property options.onError (optional)
303
330
  * @property options.onIgnore (optional) invoked whenever callback invocation is ignored by a newer invocation
@@ -309,30 +336,11 @@ type TimeoutOptions<Func extends string = 'all'> = {
309
336
  * Requires `defer`.
310
337
  * Default: `false`
311
338
  *
312
- * @returns {Function} a callback that is invoked in one of the followin 3 methods:
313
- * - sequential: when `delayMs <= 0` or `delayMs = undefined`
339
+ * @returns Callback function that can be invoked in one of the followin 3 methods:
340
+ * - sequential: when `delayMs <= 0`
314
341
  * - debounced: when `delayMs > 0` and `throttle = false`
315
342
  * - throttled: when `delayMs > 0` and `throttle = true`
316
343
  *
317
- * The main difference is that:
318
- * - Notes:
319
- * 1. A "request" simply means invokation of the returned callback function
320
- * 2. By "handled" it means a "request" will be resolved or rejected.
321
- * - `PromisE.deferred` is to be used with promises/functions
322
- * - There is no specific time delay.
323
- * - The time when a request is completed is irrelevant.
324
- * - If not throttled:
325
- * 1. Once a request is handled, all previous requests will be ignored and pool starts anew.
326
- * 2. If a function is provided in the returned callback, ALL of them will be invoked, regardless of pool size.
327
- * 3. The last/only request in an on-going requests' pool will handled (resolve/reject).
328
- * - If throttled:
329
- * 1. Once a requst starts executing, subsequent requests will be added to a queue.
330
- * 2. The last/only item in the queue will be handled. Rest will be ignored.
331
- * 3. If a function is provided in the returned callback, it will be invoked only if the request is handled.
332
- * Thus, improving performance by avoiding unnecessary invokations.
333
- * 4. If every single request/function needs to be invoked, avoid using throttle.
334
- *
335
- * - If throttled and `strict` is truthy, all subsequent request while a request is being handled will be ignored.
336
344
  *
337
345
  * @example Explanation & example usage:
338
346
  * ```typescript
@@ -360,7 +368,14 @@ type TimeoutOptions<Func extends string = 'all'> = {
360
368
  * // `5000` will be printed in the console
361
369
  * ```
362
370
  */
363
- declare function deferred<T>(options?: DeferredOptions): DeferredReturn;
371
+ declare function deferred<T, ThisArg = unknown, Delay extends number = number>(options?: DeferredAsyncOptions<ThisArg, Delay>): DeferredAsyncCallback;
372
+ declare namespace deferred {
373
+ var defaults: {
374
+ delayMs: number;
375
+ resolveError: ResolveError.REJECT;
376
+ resolveIgnored: ResolveIgnored.WITH_LAST;
377
+ };
378
+ }
364
379
 
365
380
  /**
366
381
  * @function PromisE.deferredCallback
@@ -394,18 +409,22 @@ declare function deferred<T>(options?: DeferredOptions): DeferredReturn;
394
409
  * // 200, 600, 1100
395
410
  * ```
396
411
  */
397
- declare function deferredCallback<TDefault, CbArgs extends unknown[] = unknown[]>(callback: (...args: CbArgs) => TDefault | Promise<TDefault>, options?: DeferredOptions): <TResult = TDefault>(...args: CbArgs) => IPromisE<TResult>;
412
+ 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>;
398
413
 
399
414
  /**
400
- * @function PromisE.delay
401
- * @summary Creates a promise that completes after given delay/duration.
415
+ * Creates a promise that completes after given delay/duration.
416
+ *
417
+ * Also accessible from the `PromisE` class as `PromisE.delay()`.
418
+ *
419
+ * @param duration duration in milliseconds. Default: `100`
420
+ * @param result (optional) specify a value to resolve or error to reject with.
402
421
  *
403
- * @param {Number} duration duration in milliseconds
404
- * @param {unknown} result (optional) specify a value to resolve or reject with.
405
- * Default: `delayMs` when resolved or timed out error when rejected
406
- * @param {boolean} asRejected (optional) if `true`, will reject the promise after the delay.
422
+ * Alternatively, a function (with no arguments) can be provided that returns the result.
407
423
  *
408
- * @returns See {@link IPromisE_Delay}
424
+ * Default: `delayMs` when resolved or timed out error when rejected
425
+ * @param asRejected (optional) if `true`, will reject the promise after the delay.
426
+ *
427
+ * @returns a promise
409
428
  *
410
429
  * @example Delay before continuing execution
411
430
  * ```typescript
@@ -426,6 +445,14 @@ declare function deferredCallback<TDefault, CbArgs extends unknown[] = unknown[]
426
445
  * ```
427
446
  */
428
447
  declare function delay<T = number, TReject extends boolean = boolean>(duration?: number, result?: T | (() => T), asRejected?: TReject): IPromisE_Delay<T>;
448
+ declare namespace delay {
449
+ var defaults: {
450
+ /** Default delay duration in milliseconds */
451
+ duration: number;
452
+ /** Default timed out message (if `result` is not provided) */
453
+ delayTimeoutMsg: string;
454
+ };
455
+ }
429
456
 
430
457
  /**
431
458
  * @function PromisE.delayReject
@@ -462,14 +489,16 @@ declare function delay<T = number, TReject extends boolean = boolean>(duration?:
462
489
  declare function delayReject<T = never>(duration: number, reason?: unknown): IPromisE_Delay<T>;
463
490
 
464
491
  /**
465
- * @function PromisE.timeout
466
- * @summary times out a promise after specified timeout duration.
492
+ * Creates a new promise that wraps one or more promises and rejects if they do not settle within a
493
+ * specified timeout duration. When multiple promises are provided, they can be processed using methods like
494
+ * `all` (default), `race`, `any`, or `allSettled`.
495
+ *
496
+ * @param timeout (optional) timeout duration in milliseconds.
497
+ * Default: `10000` (10 seconds)
467
498
  *
468
- * @param timeout (optional) timeout duration in milliseconds.
469
- * Default: `10000` (10 seconds)
470
- * @param values promise/function: one or more promises as individual arguments
499
+ * @param values rest param containing one or more promises/values
471
500
  *
472
- * @example Example 1: single promise - resolved
501
+ * @example Wokring with a single promise: resolved succesfully
473
502
  * ```typescript
474
503
  * PromisE.timeout(
475
504
  * 5000, // timeout after 5000ms
@@ -478,7 +507,16 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
478
507
  * // Result: 1000
479
508
  * ```
480
509
  *
481
- * @example Example 2: multiple promises - resolved
510
+ * @example Promise times out & rejected
511
+ * ```typescript
512
+ * PromisE.timeout(
513
+ * 5000, // timeout after 5000ms
514
+ * PromisE.delay(20000), // resolves after 20000ms with value 20000
515
+ * ).catch(console.error)
516
+ * // Error: Error('Timed out after 5000ms')
517
+ *```
518
+ *
519
+ * @example Working with multiple promises, resolved using "PromisE.all()"
482
520
  *
483
521
  * ```typescript
484
522
  * PromisE.timeout(
@@ -490,17 +528,8 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
490
528
  * // Result: [ 1000, 2000, 3000 ]
491
529
  * ```
492
530
  *
493
- * @example Example 3: timed out & rejected
494
- * ```typescript
495
- * PromisE.timeout(
496
- * 5000, // timeout after 5000ms
497
- * PromisE.delay(20000), // resolves after 20000ms with value 20000
498
- * ).catch(console.error)
499
- * // Error: Error('Timed out after 5000ms')
500
- *```
501
- *
502
- * @example Example 4: timed out & but not rejected.
503
- * // Eg: when API request is taking longer than expected, print a message but not reject the promise.
531
+ * @example Promise times out & but not rejected.
532
+ * Eg: when API request is taking longer than expected, print a message avoid rejecting the promise.
504
533
  * ```typescript
505
534
  * const promise = PromisE.timeout(
506
535
  * 5000, // timeout after 5000ms
@@ -512,13 +541,48 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
512
541
  *
513
542
  * // promise timed out >> print/update UI
514
543
  * console.log('Request is taking longer than expected......')
515
- * // now return the data promise (the promise(s) provided in the PromisE.timeout())
544
+ * // Now return the data promise which is the result of `PromisE.all(promises)` (default).
516
545
  * return promise.data
517
546
  * })
518
547
  *```
548
+ *
549
+ * @example Multiple promises resolved using "PromisE.race()"
550
+ *
551
+ * ```typescript
552
+ * PromisE.timeout(
553
+ * { // instead of `timeout: number` an object can be used for additional options
554
+ * func: 'race', // tells PromisE.timeout to use `PromisE.race(promises)`
555
+ * timeout: 5000, // timeout after 5000ms
556
+ * timeoutMsg: 'My custom timed out message',
557
+ * },
558
+ * PromisE.delay(1000), // resolves after 1000ms with value 1000
559
+ * PromisE.delay(2000), // resolves after 2000ms with value 2000
560
+ * PromisE.delay(3000), // resolves after 3000ms with value 3000
561
+ * ).then(console.log)
562
+ * // Result: 1000 (Result of `Promise.race(promises)`)
563
+ * ```
564
+ */
565
+ declare function timeout<T extends [unknown, ...unknown[]], // require at least one value
566
+ Result = T['length'] extends 1 ? Awaited<T[0]> : Awaited<T[number]>[]>(timeout: number, ...values: T): IPromisE_Timeout<Result>;
567
+ /**
568
+ *
569
+ * @param options An options object can be passed with one or more of the following properties:
570
+ * @param options.func (optional) Name of the `PromisE` method to be used to combine the `values`.
571
+ * Only used when more than one promise is provided.
572
+ *
573
+ * Accepted values:
574
+ * 1. `'all'` **(default)**: for `PromisE.all`
575
+ * 2. `'allSettled'`: for `PromisE.allSettled`
576
+ * 3. `'any'`: for `PromisE.any`
577
+ * 4. `'race'`: for `PromisE.race`
578
+ *
579
+ * @param options.timeout (optional) timeout duration in milliseconds. Default: `10_000` (10 seconds)
580
+ * @param options.timeoutMsg (optional) custom error message to be used when promises timeout.
581
+ *
582
+ * @param values
519
583
  */
520
584
  declare function timeout<T extends [unknown, ...unknown[]], // require at least one value
521
- TFunc extends keyof TimeoutFunc<T>, Result = T['length'] extends 1 ? Awaited<T[0]> : Awaited<ReturnType<TimeoutFunc<T>[TFunc]>>>(timeout: number | TimeoutOptions<TFunc>, ...values: T): IPromisE_Timeout<Result>;
585
+ TFunc extends keyof TimeoutFunc<T>, Result = T['length'] extends 1 ? Awaited<T[0]> : Awaited<ReturnType<TimeoutFunc<T>[TFunc]>>>(options: TimeoutOptions<TFunc>, ...values: T): IPromisE_Timeout<Result>;
522
586
  declare namespace timeout {
523
587
  var defaultOptions: Required<TimeoutOptions>;
524
588
  }
@@ -564,29 +628,25 @@ declare namespace timeout {
564
628
  * ```
565
629
  */
566
630
  declare class PromisE<T = unknown> extends PromisEBase<T> {
567
- /** Global configuration & default values */
568
- static config: {
569
- deferOptions: DeferredOptions;
570
- delayTimeoutMsg: string;
571
- retryOptions: {
631
+ static deferred: typeof deferred;
632
+ static deferredCallback: typeof deferredCallback;
633
+ static delay: typeof delay;
634
+ static delayReject: typeof delayReject;
635
+ static retry: {
636
+ <T_1>(func: () => _superutils_core.ValueOrPromise<T_1>, options: RetryOptions<T_1>): Promise<T_1>;
637
+ defaults: {
572
638
  retry: number;
573
639
  retryBackOff: "exponential";
574
640
  retryDelay: number;
575
641
  retryDelayJitter: true;
576
642
  retryDelayJitterMax: number;
577
- retryIf: null;
578
643
  };
579
644
  };
580
- static deferred: typeof deferred;
581
- static deferredCallback: typeof deferredCallback;
582
- static delay: typeof delay;
583
- static delayReject: typeof delayReject;
584
- static retry: <T_1>(func: () => _superutils_core.ValueOrPromise<T_1>, options?: RetryOptions<T_1>) => Promise<T_1>;
585
645
  static timeout: typeof timeout;
586
646
  }
587
647
 
588
648
  /**
589
- * Executes a function and retries it on failure or until a specific condition is met.
649
+ * Executes a function asynchronously and retries it on failure or until a specific condition is met.
590
650
  *
591
651
  * The function will be re-executed if:
592
652
  * 1. The `func` promise rejects or the function throws an error.
@@ -596,19 +656,63 @@ declare class PromisE<T = unknown> extends PromisEBase<T> {
596
656
  * Retries will stop when the `retry` count is exhausted, or when `func` executes successfully
597
657
  * (resolves without error) AND the `retryIf` (if provided) returns `false`.
598
658
  *
599
- * @template T The type of the value that the `func` returns/resolves to.
600
- * @param {() => ValueOrPromise<T>} func The function to execute. It can be synchronous or asynchronous.
601
- * @param {RetryOptions} [options={}] (optional) Options for configuring the retry mechanism.
602
- * @property {number} [options.retry=1] (optional) The maximum number of retries.
603
- * @property {number} [options.retryDelayMs=300] The base delay in milliseconds between retries.
604
- * @property {'exponential' | 'fixed'} [options.retryBackOff='exponential'] The backoff strategy. 'exponential' doubles the delay for each subsequent retry. 'fixed' uses a constant delay.
605
- * @property {boolean} [options.retryDelayJitter=true] If true, adds a random jitter to the delay to prevent thundering herd problem.
606
- * @property {number} [options.retryDelayJitterMax=100] The maximum jitter in milliseconds to add to the delay.
607
- * @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.
608
- * @returns {Promise<T | undefined>} A promise that resolves with the result of the last successful execution of `func`.
609
- * If all retries fail (either by throwing an error or by the condition function always returning true),
610
- * it resolves with `undefined`. Errors thrown by `func` are caught and handled internally, not re-thrown.
659
+ * @template T The type of the value that the `func` resolves to.
660
+ *
661
+ * @param func The function to execute. It can be synchronous or asynchronous.
662
+ * @param options (optional) Configuration of the retry mechanism.
663
+ *
664
+ * The following options' default values can be configured to be EFFECTIVE GLOBALLY.
665
+ *
666
+ * ```typescript
667
+ * PromisE.retry.defaults = {
668
+ * retry: 1,
669
+ * retryBackOff: 'exponential',
670
+ * retryDelay: 300,
671
+ * retryDelayJitter: true,
672
+ * retryDelayJitterMax: 100,
673
+ * }
674
+ * ```
675
+ * @param options.retry (optional) The maximum number of retries. Default: `1`
676
+ * @param options.retryBackOff (optional) The backoff strategy.
677
+ * Accepted values:
678
+ * - `'exponential'` doubles the delay for each subsequent retry.
679
+ * - `'linear'` uses a constant delay.
680
+ *
681
+ * Default: `'exponential'`
682
+ * @param options.retryDelayMs (optional) The base delay in milliseconds between retries.
683
+ *
684
+ * Default: `300`
685
+ * @param options.retryDelayJitter (optional) If true, adds a random jitter to the delay to prevent
686
+ * the thundering herd problem.
687
+ *
688
+ * Default: `true`
689
+ * @param options.retryDelayJitterMax The maximum jitter in milliseconds to add to the delay.
690
+ *
691
+ * Default: `100`
692
+ * @param options.retryIf (optional) A function that is called after a successful execution of `func`.
693
+ * If it returns `true`, a retry is triggered.
694
+ *
695
+ * **Arguments:**
696
+ * - `result`: result received after the most recent `func` excution
697
+ * - `retryCount`: number of times the execution has been retried (`total_attemts - 1`)
698
+ *
699
+ * If `retryIf()` throws error, the are handled gracefully and it's return value defaults `false` (no further retry).
700
+ *
701
+ * @returns A promise that resolves with the result of the last successful execution of `func`.
702
+ * If all retries fail (either by throwing an error or when `retryIf()` returned true in every time),
703
+ * it resolves with the last return value of `func()`. Errors thrown by `func` are caught and handled internally,
704
+ * only re-thrown if no result is received after maximum retries.
611
705
  */
612
- declare const retry: <T>(func: () => ValueOrPromise<T>, options?: RetryOptions<T>) => Promise<T>;
706
+ declare const retry: {
707
+ <T>(func: () => ValueOrPromise<T>, options: RetryOptions<T>): Promise<T>;
708
+ /** Global default values */
709
+ defaults: {
710
+ retry: number;
711
+ retryBackOff: "exponential";
712
+ retryDelay: number;
713
+ retryDelayJitter: true;
714
+ retryDelayJitterMax: number;
715
+ };
716
+ };
613
717
 
614
- export { type Config, type DeferredOptions, type DeferredReturn, type IPromisE, type IPromisE_Delay, type IPromisE_Timeout, type OnEarlyFinalize, PromisE, PromisEBase, type PromiseParams, ResolveError, ResolveIgnored, type RetryOptions, type TimeoutFunc, type TimeoutOptions, config, PromisE as default, deferred, deferredCallback, delay, delayReject, retry, timeout };
718
+ export { type DeferredAsyncCallback, type DeferredAsyncDefaults, type DeferredAsyncGetPromise, type DeferredAsyncOptions, type IPromisE, type IPromisE_Delay, type IPromisE_Timeout, type OnEarlyFinalize, PromisE, PromisEBase, type PromiseParams, ResolveError, ResolveIgnored, type RetryOptions, type TimeoutFunc, type TimeoutOptions, PromisE as default, deferred, deferredCallback, delay, delayReject, retry, timeout };
package/dist/index.js CHANGED
@@ -1,46 +1,10 @@
1
- // src/types/deferred.ts
2
- var ResolveError = /* @__PURE__ */ ((ResolveError2) => {
3
- ResolveError2["NEVER"] = "NEVER";
4
- ResolveError2["REJECT"] = "REJECT";
5
- ResolveError2["WITH_ERROR"] = "RESOLVE_ERROR";
6
- ResolveError2["WITH_UNDEFINED"] = "RESOLVE_UNDEFINED";
7
- return ResolveError2;
8
- })(ResolveError || {});
9
- var ResolveIgnored = /* @__PURE__ */ ((ResolveIgnored2) => {
10
- ResolveIgnored2["NEVER"] = "NEVER";
11
- ResolveIgnored2["WITH_LAST"] = "WITH_LAST";
12
- ResolveIgnored2["WITH_UNDEFINED"] = "WITH_UNDEFINED";
13
- return ResolveIgnored2;
14
- })(ResolveIgnored || {});
15
-
16
- // src/config.ts
17
- var config = {
18
- /** Default value for `options` used by `PromisE.*deferred*` functions */
19
- deferOptions: {
20
- delayMs: 100,
21
- resolveError: "REJECT" /* REJECT */,
22
- resolveIgnored: "WITH_LAST" /* WITH_LAST */,
23
- throttle: false
24
- },
25
- delayTimeoutMsg: "Timed out after",
26
- retryOptions: {
27
- retry: 1,
28
- retryBackOff: "exponential",
29
- retryDelay: 300,
30
- retryDelayJitter: true,
31
- retryDelayJitterMax: 100,
32
- retryIf: null
33
- }
34
- };
35
- var config_default = config;
36
-
37
1
  // src/deferred.ts
38
2
  import {
39
3
  deferred as deferredCore,
40
4
  fallbackIfFails as fallbackIfFails2,
41
- forceCast,
42
5
  isFn as isFn2,
43
6
  isPositiveNumber,
7
+ objCopy,
44
8
  throttled as throttledCore
45
9
  } from "@superutils/core";
46
10
 
@@ -185,17 +149,33 @@ _PromisEBase.withResolvers = () => {
185
149
  var PromisEBase = _PromisEBase;
186
150
  var PromisEBase_default = PromisEBase;
187
151
 
152
+ // src/types/deferred.ts
153
+ var ResolveError = /* @__PURE__ */ ((ResolveError2) => {
154
+ ResolveError2["NEVER"] = "NEVER";
155
+ ResolveError2["REJECT"] = "REJECT";
156
+ ResolveError2["WITH_ERROR"] = "RESOLVE_ERROR";
157
+ ResolveError2["WITH_UNDEFINED"] = "RESOLVE_UNDEFINED";
158
+ return ResolveError2;
159
+ })(ResolveError || {});
160
+ var ResolveIgnored = /* @__PURE__ */ ((ResolveIgnored2) => {
161
+ ResolveIgnored2["NEVER"] = "NEVER";
162
+ ResolveIgnored2["WITH_LAST"] = "WITH_LAST";
163
+ ResolveIgnored2["WITH_UNDEFINED"] = "WITH_UNDEFINED";
164
+ return ResolveIgnored2;
165
+ })(ResolveIgnored || {});
166
+
188
167
  // src/deferred.ts
189
- function deferred(options = {}) {
190
- const defaults = config_default.deferOptions;
168
+ function deferred(options) {
169
+ const { defaults } = deferred;
170
+ options = objCopy(defaults, options, [], "empty");
191
171
  let { onError, onIgnore, onResult } = options;
192
172
  const {
193
- delayMs = defaults.delayMs,
194
- resolveError = defaults.resolveError,
173
+ delayMs,
174
+ resolveError,
195
175
  // by default reject on error
196
- resolveIgnored = defaults.resolveIgnored,
176
+ resolveIgnored,
197
177
  thisArg,
198
- throttle = defaults.throttle
178
+ throttle
199
179
  } = options;
200
180
  let lastPromisE = null;
201
181
  const queue = /* @__PURE__ */ new Map();
@@ -273,10 +253,15 @@ function deferred(options = {}) {
273
253
  qItem.started = false;
274
254
  queue.set(id, qItem);
275
255
  if (gotDelay || !lastPromisE) execute(id, qItem);
276
- return forceCast(qItem);
256
+ return qItem;
277
257
  };
278
258
  return deferredFunc;
279
259
  }
260
+ deferred.defaults = {
261
+ delayMs: 100,
262
+ resolveError: "REJECT" /* REJECT */,
263
+ resolveIgnored: "WITH_LAST" /* WITH_LAST */
264
+ };
280
265
  var deferred_default = deferred;
281
266
 
282
267
  // src/deferredCallback.ts
@@ -290,7 +275,7 @@ var deferredCallback_default = deferredCallback;
290
275
 
291
276
  // src/delay.ts
292
277
  import { fallbackIfFails as fallbackIfFails3, isFn as isFn3 } from "@superutils/core";
293
- function delay(duration = 100, result = duration, asRejected = false) {
278
+ function delay(duration = delay.defaults.duration, result = duration, asRejected = false) {
294
279
  const promise = new PromisEBase_default();
295
280
  const finalize = (result2) => {
296
281
  var _a;
@@ -298,7 +283,9 @@ function delay(duration = 100, result = duration, asRejected = false) {
298
283
  result2 = (_a = fallbackIfFails3(result2, [], void 0)) != null ? _a : duration;
299
284
  if (!asRejected) return promise.resolve(result2);
300
285
  promise.reject(
301
- result2 != null ? result2 : new Error(`${config_default.delayTimeoutMsg} ${duration}ms`)
286
+ duration !== result2 && result2 !== void 0 ? result2 : new Error(
287
+ `${delay.defaults.delayTimeoutMsg} ${duration}ms`
288
+ )
302
289
  );
303
290
  };
304
291
  promise.timeoutId = setTimeout(() => finalize(result), duration);
@@ -307,6 +294,12 @@ function delay(duration = 100, result = duration, asRejected = false) {
307
294
  }).finally(() => promise.pause());
308
295
  return promise;
309
296
  }
297
+ delay.defaults = {
298
+ /** Default delay duration in milliseconds */
299
+ duration: 100,
300
+ /** Default timed out message (if `result` is not provided) */
301
+ delayTimeoutMsg: "Timed out after"
302
+ };
310
303
  var delay_default = delay;
311
304
 
312
305
  // src/delayReject.ts
@@ -316,42 +309,65 @@ function delayReject(duration, reason) {
316
309
  var delayReject_default = delayReject;
317
310
 
318
311
  // src/retry.ts
319
- import { isPositiveInteger } from "@superutils/core";
320
- var retry = async (func, options = {}) => {
321
- const d = config_default.retryOptions;
312
+ import {
313
+ fallbackIfFails as fallbackIfFails4,
314
+ isEmpty,
315
+ isPositiveInteger,
316
+ objCopy as objCopy2
317
+ } from "@superutils/core";
318
+ var retry = async (func, options) => {
319
+ options = objCopy2(retry.defaults, options != null ? options : {}, [], (key, value) => {
320
+ switch (key) {
321
+ // case 'retryDelayJitter':
322
+ // return true
323
+ case "retry":
324
+ // eslint-disable-next-line no-fallthrough
325
+ case "retryDelay":
326
+ case "retryDelayJitterMax":
327
+ return value !== 0 && !isPositiveInteger(value);
328
+ }
329
+ return !!isEmpty(value);
330
+ });
322
331
  const {
323
- retryIf,
324
- retryBackOff = d.retryBackOff,
325
- retryDelayJitter = d.retryDelayJitter
332
+ retry: maxRetries,
333
+ retryBackOff,
334
+ retryDelay,
335
+ retryDelayJitter,
336
+ retryDelayJitterMax
326
337
  } = options;
327
- let {
328
- retry: maxRetries = d.retry,
329
- retryDelay: delayMs,
330
- retryDelayJitterMax: jitterMax
331
- } = options;
332
- if (maxRetries !== 0 && !isPositiveInteger(maxRetries)) maxRetries = d.retry;
333
- if (!isPositiveInteger(delayMs)) delayMs = d.retryDelay;
334
- if (!isPositiveInteger(jitterMax)) jitterMax = d.retryDelayJitterMax;
338
+ let _retryDelay = retryDelay;
335
339
  let retryCount = -1;
336
340
  let result;
337
341
  let error;
338
342
  let shouldRetry = false;
339
343
  do {
340
344
  retryCount++;
341
- if (retryBackOff === "exponential" && retryCount > 1) delayMs *= 2;
342
- if (retryDelayJitter) delayMs += Math.floor(Math.random() * jitterMax);
343
- retryCount > 0 && await delay_default(delayMs);
345
+ if (retryBackOff === "exponential" && retryCount > 1) _retryDelay *= 2;
346
+ if (retryDelayJitter)
347
+ _retryDelay += Math.floor(Math.random() * retryDelayJitterMax);
348
+ retryCount > 0 && await delay_default(_retryDelay);
344
349
  try {
345
350
  error = void 0;
346
351
  result = await func();
347
352
  } catch (err) {
348
353
  error = err;
349
354
  }
350
- shouldRetry = maxRetries > 0 && retryCount < maxRetries && (!!error || !!(retryIf == null ? void 0 : retryIf(result, retryCount, error)));
355
+ shouldRetry = maxRetries > 0 && retryCount < maxRetries && (!!error || !!await fallbackIfFails4(
356
+ options.retryIf,
357
+ [result, retryCount],
358
+ false
359
+ ));
351
360
  } while (shouldRetry);
352
361
  if (error !== void 0) return Promise.reject(error);
353
362
  return result;
354
363
  };
364
+ retry.defaults = {
365
+ retry: 1,
366
+ retryBackOff: "exponential",
367
+ retryDelay: 300,
368
+ retryDelayJitter: true,
369
+ retryDelayJitterMax: 100
370
+ };
355
371
  var retry_default = retry;
356
372
 
357
373
  // src/timeout.ts
@@ -397,8 +413,6 @@ var timeout_default = timeout;
397
413
  // src/PromisE.ts
398
414
  var PromisE = class extends PromisEBase_default {
399
415
  };
400
- /** Global configuration & default values */
401
- PromisE.config = config_default;
402
416
  PromisE.deferred = deferred_default;
403
417
  PromisE.deferredCallback = deferredCallback_default;
404
418
  PromisE.delay = delay_default;
@@ -414,7 +428,6 @@ export {
414
428
  PromisEBase,
415
429
  ResolveError,
416
430
  ResolveIgnored,
417
- config,
418
431
  index_default as default,
419
432
  deferred,
420
433
  deferredCallback,
package/package.json CHANGED
@@ -4,9 +4,9 @@
4
4
  "url": "https://github.com/alien45/superutils/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@superutils/core": "1.0.3"
7
+ "@superutils/core": "^1.0.5"
8
8
  },
9
- "description": "An extended Promise class with extra features and utilities.",
9
+ "description": "An extended Promise with extra features such as status tracking, deferred/throttled execution, timeout and retry mechanism.",
10
10
  "files": [
11
11
  "dist",
12
12
  "README.md",
@@ -23,7 +23,7 @@
23
23
  "main": "dist/index.js",
24
24
  "name": "@superutils/promise",
25
25
  "peerDpendencies": {
26
- "@superutils/core": "1.0.3"
26
+ "@superutils/core": "^1.0.5"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
@@ -37,10 +37,11 @@
37
37
  "_watch": "tsc -p tsconfig.json --watch",
38
38
  "build": "tsup src/index.ts --format esm --dts --clean --config ../../tsup.config.js",
39
39
  "dev": "npm run build -- --watch",
40
+ "start": "npm run build -- --watch",
40
41
  "test": "cd ../../ && npm run test promise"
41
42
  },
42
43
  "sideEffects": false,
43
44
  "type": "module",
44
45
  "types": "dist/index.d.ts",
45
- "version": "1.0.3"
46
+ "version": "1.0.6"
46
47
  }