@superutils/promise 1.1.6 → 1.2.1

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 +205 -104
  2. package/dist/index.js +156 -50
  3. package/package.json +4 -3
package/dist/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import * as _superutils_core from '@superutils/core';
2
- import { ValueOrPromise, TimeoutId, PositiveNumber, DeferredOptions } from '@superutils/core';
2
+ import { ValueOrPromise, PositiveNumber, DeferredOptions, TimeoutId } from '@superutils/core';
3
3
 
4
4
  interface IPromisE<T = unknown> extends Promise<T> {
5
5
  /** 0: pending, 1: resolved, 2: rejected */
6
6
  readonly state: 0 | 1 | 2;
7
7
  /** callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve/reject methods */
8
8
  onEarlyFinalize: OnEarlyFinalize<T>[];
9
+ onFinalize: OnFinalize<T>[];
9
10
  /** Indicates if the promise is still pending/unfinalized */
10
11
  readonly pending: boolean;
11
12
  /** Reject pending promise early. */
@@ -17,64 +18,8 @@ interface IPromisE<T = unknown> extends Promise<T> {
17
18
  /** Indicates if the promise has been resolved */
18
19
  readonly resolved: boolean;
19
20
  }
20
- interface IPromisE_Delay<T = unknown> extends Promise<T>, IPromisE<T> {
21
- /**
22
- * Caution: pausing will prevent the promise from resolving/rejeting automatically.
23
- *
24
- * In order to finalize the promise either the `resolve()` or the `reject()` method must be invoked manually.
25
- *
26
- * An never-finalized promise may cause memory leak and will leave it at the mercry of the garbage collector.
27
- * Use `pause()` only if you are sure.
28
- *
29
- * @example
30
- * ```typescript
31
- * // Example 1: SAFE => no memory leak, because no reference to the promise is stored and no suspended code
32
- * <button onClick={() => {
33
- * const promise = PromisE.delay(1000).then(... do stuff ....)
34
- * setTimeout(() => promise.pause(), 300)
35
- * }}>Click Me</button>
36
- * ```
37
- *
38
- * @example UNSAFE => potential memory leak, because of suspended code
39
- * ```typescript
40
- * <button onClick={() => {
41
- * const promise = PromisE.delay(1000)
42
- * setTimeout(() => promise.pause(), 300)
43
- * await promise // suspended code
44
- * //... do stuff ....
45
- * }}>Click Me</button>
46
- * ```
47
- *
48
- * @example UNSAFE => potential memory leak, because of preserved reference.
49
- * ```typescript
50
- * // Until the reference to promises is collected by the garbage collector,
51
- * // reference to the unfinished promise will remain in memory.
52
- * const promises = []
53
- * <button onClick={() => {
54
- * const promise = PromisE.delay(1000)
55
- * setTimeout(() => promise.pause(), 300)
56
- * promises.push(promise)
57
- * }}>Click Me</button>
58
- * ```
59
- */
60
- pause: () => void;
61
- /** Timeout ID */
62
- timeoutId: TimeoutId;
63
- }
64
- /**
65
- * Descibes a timeout PromisE and it's additional properties.
66
- */
67
- type IPromisE_Timeout<T = unknown> = IPromisE<T> & {
68
- /** Clearing the timeout will prevent it from timing out */
69
- clearTimeout: () => void;
70
- /** The result/data promise. If more than one supplied in `args` result promise will be a combined `PromisE.all` */
71
- data: IPromisE<T>;
72
- /** A shorthand getter to check if the promise has timed out. Same as `promise.timeout.rejected`. */
73
- readonly timedout: boolean;
74
- /** The timeout promise */
75
- timeout: IPromisE_Delay<T>;
76
- };
77
21
  type OnEarlyFinalize<T> = <TResolved extends boolean, TValue = TResolved extends true ? T : unknown>(resolved: TResolved, resultOrReason: TValue) => ValueOrPromise<unknown>;
22
+ type OnFinalize<T> = (result?: T | PromiseLike<T>, error?: unknown) => ValueOrPromise<void>;
78
23
  type PromiseParams<T = unknown> = ConstructorParameters<typeof Promise<T>>;
79
24
 
80
25
  /** Return type of `PromisE.deferred()` */
@@ -96,7 +41,7 @@ type DeferredAsyncOptions<ThisArg = unknown, Delay = unknown> = {
96
41
  delayMs?: 0 | PositiveNumber<Delay>;
97
42
  /**
98
43
  * Whether to ignore (based on `resolveIgnored` settings) stale promises.
99
- * In debouce/throttle mode, when an older promise is resolved after a newly resolved promise,
44
+ * In debounce/throttle mode, when an older promise is resolved after a newly resolved promise,
100
45
  * the older one is considered stale.
101
46
  *
102
47
  * Default: `false`
@@ -125,17 +70,13 @@ type DeferredAsyncOptions<ThisArg = unknown, Delay = unknown> = {
125
70
  * See {@link ResolveError} for available options.
126
71
  */
127
72
  resolveError?: ResolveError;
128
- } & (({
129
- delayMs: 0;
130
- } & {
73
+ } & (Delay extends 0 ? {
131
74
  throttle?: false;
132
75
  /** Callback invoked whenever promise/function throws error */
133
76
  onError?: (this: ThisArg, err: unknown) => ValueOrPromise<unknown>;
134
77
  /** The value to be used as "thisArg" whenever any of the callbacks are invoked */
135
78
  thisArg?: ThisArg;
136
- }) | ({
137
- delayMs?: PositiveNumber<Delay>;
138
- } & DeferredOptions<ThisArg>));
79
+ } : DeferredOptions<ThisArg>);
139
80
  /** Determines what to do when deferred promise/function fails */
140
81
  declare enum ResolveError {
141
82
  /** Neither resolve nor reject the failed */
@@ -160,6 +101,51 @@ declare enum ResolveIgnored {
160
101
  WITH_UNDEFINED = "WITH_UNDEFINED"
161
102
  }
162
103
 
104
+ interface IPromisE_Delay<T = unknown> extends Promise<T>, IPromisE<T> {
105
+ /**
106
+ * Caution: pausing will prevent the promise from resolving/rejeting automatically.
107
+ *
108
+ * In order to finalize the promise either the `resolve()` or the `reject()` method must be invoked manually.
109
+ *
110
+ * An never-finalized promise may cause memory leak and will leave it at the mercry of the garbage collector.
111
+ * Use `pause()` only if you are sure.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * // Example 1: SAFE => no memory leak, because no reference to the promise is stored and no suspended code
116
+ * <button onClick={() => {
117
+ * const promise = PromisE.delay(1000).then(... do stuff ....)
118
+ * setTimeout(() => promise.pause(), 300)
119
+ * }}>Click Me</button>
120
+ * ```
121
+ *
122
+ * @example UNSAFE => potential memory leak, because of suspended code
123
+ * ```typescript
124
+ * <button onClick={() => {
125
+ * const promise = PromisE.delay(1000)
126
+ * setTimeout(() => promise.pause(), 300)
127
+ * await promise // suspended code
128
+ * //... do stuff ....
129
+ * }}>Click Me</button>
130
+ * ```
131
+ *
132
+ * @example UNSAFE => potential memory leak, because of preserved reference.
133
+ * ```typescript
134
+ * // Until the reference to promises is collected by the garbage collector,
135
+ * // reference to the unfinished promise will remain in memory.
136
+ * const promises = []
137
+ * <button onClick={() => {
138
+ * const promise = PromisE.delay(1000)
139
+ * setTimeout(() => promise.pause(), 300)
140
+ * promises.push(promise)
141
+ * }}>Click Me</button>
142
+ * ```
143
+ */
144
+ pause: () => void;
145
+ /** Timeout ID */
146
+ timeoutId: TimeoutId;
147
+ }
148
+
163
149
  /** Function to determine whether retry should be attempted based on previous result/error */
164
150
  type RetryIfFunc<T = unknown> = (prevResult: T | undefined, retryCount: number, error?: unknown) => ValueOrPromise<boolean | void>;
165
151
  /** Options for automatic retry mechanism */
@@ -225,6 +211,7 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
225
211
  /**
226
212
  * callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve()/reject() methods */
227
213
  onEarlyFinalize: OnEarlyFinalize<T>[];
214
+ onFinalize: OnFinalize<T>[];
228
215
  /** Create a PromisE instance as a drop-in replacement for Promise */
229
216
  constructor(...args: PromiseParams<T>);
230
217
  /** Extend an existing Promise instance to check status or finalize early */
@@ -273,7 +260,7 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
273
260
  /** Sugar for `new PromisE(Promise.race(..))` */
274
261
  static race: <T_1 extends unknown[]>(values: T_1) => IPromisE<Awaited<T_1[number]>>;
275
262
  /** Extends Promise.reject */
276
- static reject: <T_1 = never>(reason: unknown) => IPromisE<T_1>;
263
+ static reject: <T_1 = never>(reason: unknown) => PromisEBase<T_1>;
277
264
  /** Sugar for `new PromisE(Promise.resolve(...))` */
278
265
  static resolve: <T_1>(value?: T_1 | PromiseLike<T_1>) => IPromisE<T_1>;
279
266
  /** Sugar for `new PromisE(Promise.try(...))` */
@@ -310,25 +297,94 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
310
297
  };
311
298
  }
312
299
 
313
- type TimeoutFunc<T extends unknown[]> = {
300
+ /**
301
+ * Descibes a timeout PromisE and it's additional properties.
302
+ */
303
+ type IPromisE_Timeout<T = unknown> = IPromisE<T> & {
304
+ readonly abortCtrl?: AbortController;
305
+ /** Read-only property indicating if the promise rejected because of external abort. */
306
+ readonly aborted: boolean;
307
+ /**
308
+ * Removes `abortCtrl/signal` listeners, effectively disabling external cancellation via AbortController.
309
+ */
310
+ cancelAbort: () => void;
311
+ /**
312
+ * Clears the timeout timer, preventing the promise from being rejected due to a timeout.
313
+ */
314
+ clearTimeout: () => void;
315
+ /** The underlying data promise. If multiple promises were passed to `timeout`, this represents the combined result (defaulting to `PromisE.all`). */
316
+ data: IPromisE<T>;
317
+ /** Read-only property indicating if the promise timed out. Equivalent to checking `promise.timeout.rejected`. */
318
+ readonly timedout: boolean;
319
+ /** The internal promise that handles the timeout logic. It rejects when the duration expires. */
320
+ timeout: IPromisE_Delay<T>;
321
+ };
322
+ type TimeoutResult<T extends unknown[], BatchFunc extends keyof BatchFuncs<T>, Values extends unknown[] = {
323
+ -readonly [P in keyof T]: T[P] extends (...args: unknown[]) => infer ReturnType ? ReturnType : T[P];
324
+ }> = Awaited<T['length'] extends 1 ? Values[0] : ReturnType<BatchFuncs<Values>[BatchFunc]>>;
325
+ /** Suported function names for batch operations */
326
+ type BatchFuncs<T extends unknown[] = []> = {
314
327
  all: typeof PromisEBase.all<T>;
315
328
  allSettled: typeof PromisEBase.allSettled<T>;
316
329
  any: typeof PromisEBase.any<T>;
317
330
  race: typeof PromisEBase.race<T>;
318
331
  };
319
332
  /**
320
- * `PromisE.timeout` options
333
+ * Options for `PromisE.timeout()`
321
334
  *
322
- * @param func (optional) name of the supported `PromieBase` static method. Default: `"all"`
335
+ * @param func (optional) name of the supported `PromiEBase` static method to be used to resolve
336
+ * when more than one promise/function is provided. Default: `"all"`
323
337
  * @param timeout (optional) timeout duration in milliseconds. Default: `10_000` (10 seconds)
324
- * @param timeoutMsg (optional) timeout error message. Default: `"Timed out after 10000ms"`
325
338
  *
326
339
  */
327
- type TimeoutOptions<Func extends string = 'all'> = {
328
- func: Func;
340
+ type TimeoutOptions<T extends unknown[] = [], BatchFuncName extends string = 'all'> = {
341
+ /**
342
+ * An `AbortController` instance.
343
+ *
344
+ * If provided:
345
+ * - It will be aborted automatically when the timeout occurs.
346
+ * - If it is aborted externally, the promise will be rejected and the timeout will be cleared.
347
+ */
348
+ abortCtrl?: AbortController;
349
+ /**
350
+ * Whether to call `abortCtrl.abort()` if the promise is finalized externally
351
+ * (resolved or rejected before timeout or abort).
352
+ *
353
+ * Default: `true`
354
+ */
355
+ abortOnEarlyFinalize?: boolean;
356
+ /**
357
+ * The name of the `PromisEBase` static method to use for resolving multiple promises/functions.
358
+ *
359
+ * Only applicable when more than one promise/function is provided.
360
+ */
361
+ batchFunc?: T['length'] extends 0 ? never : T['length'] extends 1 ? never : BatchFuncName;
362
+ /**
363
+ * Callback invoked when the promise is rejected due to an abort signal.
364
+ *
365
+ * Can be used to transform the abort error by returning a custom `Error` object.
366
+ */
367
+ onAbort?: () => ValueOrPromise<void | Error>;
368
+ /**
369
+ * Callback invoked when the promise times out.
370
+ *
371
+ * Can be used to transform the timeout error by returning a custom `Error` object.
372
+ */
373
+ onTimeout?: () => ValueOrPromise<void | Error>;
374
+ /**
375
+ * An `AbortSignal` to listen to.
376
+ *
377
+ * If aborted:
378
+ * - The promise will be rejected.
379
+ * - The `abortCtrl` (if provided and distinct) will be aborted.
380
+ * - The timeout will be cleared.
381
+ */
382
+ signal?: AbortSignal;
383
+ /** Timeout duration in milliseconds. */
329
384
  timeout?: number;
330
- timeoutMsg?: string;
331
385
  };
386
+ /** Default options for `PromisE.timeout()` */
387
+ type TimeoutOptionsDefault = Required<Omit<TimeoutOptions<unknown[], keyof BatchFuncs>, 'abortCtrl' | 'signal'>>;
332
388
 
333
389
  /**
334
390
  * @function PromisE.deferred
@@ -521,7 +577,7 @@ declare function deferredCallback<TDefault = unknown, ThisArg = unknown, Delay =
521
577
  * func()
522
578
  * ```
523
579
  */
524
- declare function delay<T = number, TReject extends boolean = boolean>(duration?: number, result?: T | (() => T), asRejected?: TReject): IPromisE_Delay<T>;
580
+ declare function delay<T = number, TReject extends boolean = boolean>(duration?: number, result?: T | (() => T | Promise<T>), asRejected?: TReject): IPromisE_Delay<T>;
525
581
  declare namespace delay {
526
582
  var defaults: {
527
583
  /** Default delay duration in milliseconds */
@@ -571,12 +627,14 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
571
627
  * `all` (default), `race`, `any`, or `allSettled`.
572
628
  *
573
629
  * @param timeout (optional) timeout duration in milliseconds.
574
- * Default: `10000` (10 seconds)
630
+ * Default: `10_000` (10 seconds)
575
631
  *
576
632
  * @param values rest param containing one or more promises/values
577
633
  *
578
- * @example Wokring with a single promise: resolved succesfully
634
+ * @example Working with a single promise
579
635
  * ```typescript
636
+ * import PromisE from '@supertuils/promise'
637
+ *
580
638
  * PromisE.timeout(
581
639
  * 5000, // timeout after 5000ms
582
640
  * PromisE.delay(1000), // resolves after 1000ms with value 1000
@@ -584,8 +642,21 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
584
642
  * // Result: 1000
585
643
  * ```
586
644
  *
645
+ * @example Working with a single function
646
+ * ```typescript
647
+ * import PromisE from '@supertuils/promise'
648
+ *
649
+ * PromisE.timeout(
650
+ * 5000, // timeout after 5000ms
651
+ * () => PromisE.delay(1000), // function resolves after 1000ms with value 1000
652
+ * ).then(console.log)
653
+ * // Result: 1000
654
+ * ```
655
+ *
587
656
  * @example Promise times out & rejected
588
657
  * ```typescript
658
+ * import PromisE from '@supertuils/promise'
659
+ *
589
660
  * PromisE.timeout(
590
661
  * 5000, // timeout after 5000ms
591
662
  * PromisE.delay(20000), // resolves after 20000ms with value 20000
@@ -593,13 +664,15 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
593
664
  * // Error: Error('Timed out after 5000ms')
594
665
  *```
595
666
  *
596
- * @example Working with multiple promises, resolved using "PromisE.all()"
667
+ * @example Working with multiple promises/functions, resolved using "PromisE.all()"
597
668
  *
598
669
  * ```typescript
670
+ * import PromisE from '@supertuils/promise'
671
+ *
599
672
  * PromisE.timeout(
600
673
  * 5000, // timeout after 5000ms
601
674
  * PromisE.delay(1000), // resolves after 1000ms with value 1000
602
- * PromisE.delay(2000), // resolves after 2000ms with value 2000
675
+ * () => PromisE.delay(2000), // resolves after 2000ms with value 2000
603
676
  * PromisE.delay(3000), // resolves after 3000ms with value 3000
604
677
  * ).then(console.log)
605
678
  * // Result: [ 1000, 2000, 3000 ]
@@ -608,6 +681,8 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
608
681
  * @example Promise times out & but not rejected.
609
682
  * Eg: when API request is taking longer than expected, print a message avoid rejecting the promise.
610
683
  * ```typescript
684
+ * import PromisE from '@supertuils/promise'
685
+ *
611
686
  * const promise = PromisE.timeout(
612
687
  * 5000, // timeout after 5000ms
613
688
  * PromisE.delay(20000), // data promise, resolves after 20000ms with value 20000
@@ -622,46 +697,49 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
622
697
  * return promise.data
623
698
  * })
624
699
  *```
700
+ */
701
+ declare function timeout<T extends [unknown, ...unknown[]]>(timeout: number, ...values: T): IPromisE_Timeout<TimeoutResult<T, 'all'>>;
702
+ /**
703
+ *
704
+ * @param options An options object can be passed with one or more of the following properties:
705
+ * @param options.abortCtrl (optional) AbortController to manually reject promise externally and/or to sync abort with timeout rejection
706
+ * @param options.func (optional) Name of the `PromisE` static method to be used to combine the `values`.
707
+ * Only used when more than one promise is provided. Default: `"all"`
708
+ *
709
+ * Accepted values:
710
+ * 1. `'all'` **(default)**: for `PromisE.all`
711
+ * 2. `'allSettled'`: for `PromisE.allSettled`
712
+ * 3. `'any'`: for `PromisE.any`
713
+ * 4. `'race'`: for `PromisE.race`
714
+ * @param options.onAbort (optional) Callback invoked when the promise is rejected due to an abort signal.
715
+ * Optionally, return an `Error` object to reject the promise with a custom error.
716
+ * @param options.onTimeout (optional) Callback invoked when the promise times out.
717
+ * Optionally, return an `Error` object to reject the promise with a custom error.
718
+ * @param options.signal (optional) AbortSignal to manually reject promise externally
719
+ * @param options.timeout (optional) timeout duration in milliseconds. If positive number is not provided, the default value will be used. Default: `10_000` (10 seconds)
720
+ *
721
+ * @param values Mix of promises, values and/or functions
625
722
  *
626
- * @example Multiple promises resolved using "PromisE.race()"
723
+ * @example Working with multiple promises/functions resolved using "PromisE.race()"
627
724
  *
628
725
  * ```typescript
726
+ * import PromisE from '@supertuils/promise'
727
+ *
629
728
  * PromisE.timeout(
630
729
  * { // instead of `timeout: number` an object can be used for additional options
631
730
  * func: 'race', // tells PromisE.timeout to use `PromisE.race(promises)`
632
731
  * timeout: 5000, // timeout after 5000ms
633
- * timeoutMsg: 'My custom timed out message',
634
732
  * },
635
733
  * PromisE.delay(1000), // resolves after 1000ms with value 1000
636
- * PromisE.delay(2000), // resolves after 2000ms with value 2000
734
+ * () => PromisE.delay(2000), // resolves after 2000ms with value 2000
637
735
  * PromisE.delay(3000), // resolves after 3000ms with value 3000
638
736
  * ).then(console.log)
639
737
  * // Result: 1000 (Result of `Promise.race(promises)`)
640
738
  * ```
641
739
  */
642
- declare function timeout<T extends [unknown, ...unknown[]], // require at least one value
643
- Result = T['length'] extends 1 ? Awaited<T[0]> : Awaited<T[number]>[]>(timeout: number, ...values: T): IPromisE_Timeout<Result>;
644
- /**
645
- *
646
- * @param options An options object can be passed with one or more of the following properties:
647
- * @param options.func (optional) Name of the `PromisE` method to be used to combine the `values`.
648
- * Only used when more than one promise is provided.
649
- *
650
- * Accepted values:
651
- * 1. `'all'` **(default)**: for `PromisE.all`
652
- * 2. `'allSettled'`: for `PromisE.allSettled`
653
- * 3. `'any'`: for `PromisE.any`
654
- * 4. `'race'`: for `PromisE.race`
655
- *
656
- * @param options.timeout (optional) timeout duration in milliseconds. Default: `10_000` (10 seconds)
657
- * @param options.timeoutMsg (optional) custom error message to be used when promises timeout.
658
- *
659
- * @param values
660
- */
661
- declare function timeout<T extends [unknown, ...unknown[]], // require at least one value
662
- 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>;
740
+ declare function timeout<T extends unknown[], TFunc extends keyof BatchFuncs<T> = keyof BatchFuncs<T>>(options: TimeoutOptions<T, TFunc>, ...values: T): IPromisE_Timeout<TimeoutResult<T, TFunc>>;
663
741
  declare namespace timeout {
664
- var defaultOptions: Required<TimeoutOptions>;
742
+ var defaults: Required<Omit<TimeoutOptions<unknown[], keyof BatchFuncs<[]>>, "abortCtrl" | "signal">>;
665
743
  }
666
744
 
667
745
  /**
@@ -792,4 +870,27 @@ declare const retry: {
792
870
  };
793
871
  };
794
872
 
795
- export { type DeferredAsyncCallback, type DeferredAsyncDefaults, type DeferredAsyncOptions, type GetPromiseFunc, type IPromisE, type IPromisE_Delay, type IPromisE_Timeout, type OnEarlyFinalize, PromisE, PromisEBase, type PromiseParams, ResolveError, ResolveIgnored, type RetryIfFunc, type RetryOptions, type TimeoutFunc, type TimeoutOptions, PromisE as default, deferred, deferredCallback, delay, delayReject, retry, timeout };
873
+ /** Timeout duration (in milliseconds) used as a fallback when positive number is not provided to {@link timeout} */
874
+ declare const TIMEOUT_FALLBACK = 10000;
875
+ /** Node.js setTimeout limit is 2147483647 (2^31-1). Larger values fire immediately. */
876
+ declare const TIMEOUT_MAX = 2147483647;
877
+ declare class TimeoutPromise<T> extends PromisEBase<T> implements IPromisE_Timeout<T> {
878
+ readonly data: IPromisE<T>;
879
+ readonly options: TimeoutOptions;
880
+ readonly timeout: IPromisE_Delay<T>;
881
+ readonly started: Date;
882
+ private _signals?;
883
+ constructor(data: IPromisE<T>, timeout: IPromisE_Delay<T>, options: TimeoutOptions, _signals?: AbortSignal[]);
884
+ get abortCtrl(): AbortController | undefined;
885
+ get aborted(): boolean;
886
+ cancelAbort(): void;
887
+ clearTimeout(): void;
888
+ get timedout(): boolean;
889
+ private _setup;
890
+ private _handleAbort;
891
+ private _handleEarlyFinalize;
892
+ private _handleFinalize;
893
+ static defaults: TimeoutOptionsDefault;
894
+ }
895
+
896
+ export { type BatchFuncs, type DeferredAsyncCallback, type DeferredAsyncDefaults, type DeferredAsyncOptions, type GetPromiseFunc, type IPromisE, type IPromisE_Delay, type IPromisE_Timeout, type OnEarlyFinalize, type OnFinalize, PromisE, PromisEBase, type PromiseParams, ResolveError, ResolveIgnored, type RetryIfFunc, type RetryOptions, TIMEOUT_FALLBACK, TIMEOUT_MAX, type TimeoutOptions, type TimeoutOptionsDefault, TimeoutPromise, type TimeoutResult, PromisE as default, deferred, deferredCallback, delay, delayReject, retry, timeout };
package/dist/index.js CHANGED
@@ -17,10 +17,16 @@ var _PromisEBase = class _PromisEBase extends Promise {
17
17
  _reject = (reason) => {
18
18
  reject(reason);
19
19
  this._state = 2;
20
+ this.onFinalize.forEach(
21
+ (fn) => fallbackIfFails(fn, [void 0, reason], void 0)
22
+ );
20
23
  };
21
24
  _resolve = (value) => {
22
25
  resolve(value);
23
26
  this._state = 1;
27
+ this.onFinalize.forEach(
28
+ (fn) => fallbackIfFails(fn, [value, void 0], void 0)
29
+ );
24
30
  };
25
31
  if (isFn(input)) {
26
32
  fallbackIfFails(input, [_resolve, _reject], _reject);
@@ -34,6 +40,7 @@ var _PromisEBase = class _PromisEBase extends Promise {
34
40
  /**
35
41
  * callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve()/reject() methods */
36
42
  this.onEarlyFinalize = [];
43
+ this.onFinalize = [];
37
44
  //
38
45
  //
39
46
  // --------------------------- Early resolve/reject ---------------------------
@@ -87,11 +94,6 @@ var _PromisEBase = class _PromisEBase extends Promise {
87
94
  get state() {
88
95
  return this._state;
89
96
  }
90
- // static withResolvers = <T = unknown>() => {
91
- // const pwr = globalThis.Promise.withResolvers<T>()
92
- // const promise = new PromisEBase<T>(pwr.promise) as IPromisE<T>
93
- // return { ...pwr, promise }
94
- // }
95
97
  };
96
98
  //
97
99
  //
@@ -108,8 +110,8 @@ _PromisEBase.any = (values) => new _PromisEBase(globalThis.Promise.any(values));
108
110
  _PromisEBase.race = (values) => new _PromisEBase(globalThis.Promise.race(values));
109
111
  /** Extends Promise.reject */
110
112
  _PromisEBase.reject = (reason) => {
111
- const { promise, reject } = _PromisEBase.withResolvers();
112
- queueMicrotask(() => reject(reason));
113
+ const promise = new _PromisEBase();
114
+ queueMicrotask(() => promise.reject(reason));
113
115
  return promise;
114
116
  };
115
117
  /** Sugar for `new PromisE(Promise.resolve(...))` */
@@ -312,23 +314,28 @@ var deferredCallback_default = deferredCallback;
312
314
 
313
315
  // src/delay.ts
314
316
  import { fallbackIfFails as fallbackIfFails3, isFn as isFn3 } from "@superutils/core";
315
- function delay(duration = delay.defaults.duration, result = duration, asRejected = false) {
317
+ function delay(duration = delay.defaults.duration, result, asRejected = false) {
316
318
  const promise = new PromisEBase_default();
317
319
  const finalize = (result2) => {
318
- var _a;
319
- if (isFn3(result2))
320
- result2 = (_a = fallbackIfFails3(result2, [], void 0)) != null ? _a : duration;
321
- if (!asRejected) return promise.resolve(result2);
322
- promise.reject(
323
- duration !== result2 && result2 !== void 0 ? result2 : new Error(
324
- `${delay.defaults.delayTimeoutMsg} ${duration}ms`
325
- )
320
+ const _result = fallbackIfFails3(
321
+ async () => {
322
+ const _result2 = await (isFn3(result2) ? result2() : result2);
323
+ return !asRejected ? _result2 != null ? _result2 : duration : _result2 != null ? _result2 : new Error(
324
+ `${delay.defaults.delayTimeoutMsg} ${duration}ms`
325
+ );
326
+ },
327
+ [],
328
+ // when result is a function and it fails/rejects,
329
+ // promise will reject even if `asRejected = false`
330
+ (err) => Promise.reject(err)
326
331
  );
332
+ !asRejected ? promise.resolve(_result) : _result.then(promise.reject, promise.reject);
327
333
  };
328
334
  promise.timeoutId = setTimeout(() => finalize(result), duration);
329
335
  promise.pause = () => clearTimeout(promise.timeoutId);
330
336
  promise.catch(() => {
331
337
  }).finally(() => promise.pause());
338
+ promise.onEarlyFinalize.push(() => promise.pause());
332
339
  return promise;
333
340
  }
334
341
  delay.defaults = {
@@ -411,45 +418,141 @@ retry.defaults = {
411
418
  };
412
419
  var retry_default = retry;
413
420
 
414
- // src/timeout.ts
415
- import { isFn as isFn4, isObj, isPositiveNumber as isPositiveNumber2 } from "@superutils/core";
416
- function timeout(timeout2, ...values) {
417
- var _a, _b;
418
- let funcName = "all";
419
- let timeoutMsg = "";
420
- if (isObj(timeout2)) {
421
- funcName = timeout2.func;
422
- timeoutMsg = (_a = timeout2.timeoutMsg) != null ? _a : "";
423
- timeout2 = (_b = timeout2.timeout) != null ? _b : 1e4;
421
+ // src/TimeoutPromise.ts
422
+ import { arrUnique, fallbackIfFails as fallbackIfFails5, isObj } from "@superutils/core";
423
+ var TIMEOUT_FALLBACK = 1e4;
424
+ var TIMEOUT_MAX = 2147483647;
425
+ var TimeoutPromise = class extends PromisEBase_default {
426
+ constructor(data, timeout2, options, _signals) {
427
+ super(data);
428
+ this.started = /* @__PURE__ */ new Date();
429
+ this._setup = () => {
430
+ var _a, _b, _c, _d;
431
+ this._signals = arrUnique(
432
+ [(_b = (_a = this.options) == null ? void 0 : _a.abortCtrl) == null ? void 0 : _b.signal, (_c = this.options) == null ? void 0 : _c.signal].filter(
433
+ Boolean
434
+ )
435
+ );
436
+ !this.onEarlyFinalize.includes(this._handleEarlyFinalize) && this.onEarlyFinalize.push(this._handleEarlyFinalize);
437
+ !this.onFinalize.includes(this._handleFinalize) && this.onFinalize.push(this._handleFinalize);
438
+ (_d = this._signals) == null ? void 0 : _d.forEach(
439
+ (signal) => signal == null ? void 0 : signal.addEventListener(
440
+ "abort",
441
+ this._handleAbort
442
+ )
443
+ );
444
+ };
445
+ this._handleAbort = async () => {
446
+ var _a, _b, _c;
447
+ if (!((_a = this._signals) == null ? void 0 : _a.length) || !this.pending) return;
448
+ let err = await fallbackIfFails5((_b = this.options) == null ? void 0 : _b.onAbort, [], void 0);
449
+ err != null ? err : err = new Error(
450
+ `Aborted after ${(/* @__PURE__ */ new Date()).getTime() - this.started.getTime()}ms`
451
+ );
452
+ (_c = err.name) != null ? _c : err.name = "AbortError";
453
+ this.reject(err);
454
+ };
455
+ this._handleEarlyFinalize = () => {
456
+ var _a, _b, _c, _d;
457
+ ((_a = this.options) == null ? void 0 : _a.abortOnEarlyFinalize) && ((_d = (_c = (_b = this.options) == null ? void 0 : _b.abortCtrl) == null ? void 0 : _c.abort) == null ? void 0 : _d.call(_c));
458
+ };
459
+ // cleanup after execution
460
+ this._handleFinalize = ((_, err) => {
461
+ var _a, _b, _c, _d, _e;
462
+ const failed = !this.timeout.rejected && !((_a = this._signals) == null ? void 0 : _a.find((x) => x == null ? void 0 : x.aborted));
463
+ if (failed) return;
464
+ ((_d = (_c = (_b = this.options) == null ? void 0 : _b.abortCtrl) == null ? void 0 : _c.signal) == null ? void 0 : _d.aborted) === false && ((_e = this.options) == null ? void 0 : _e.abortCtrl.abort(err));
465
+ this.cancelAbort();
466
+ this.clearTimeout();
467
+ });
468
+ this.data = data;
469
+ this.options = isObj(options) ? options : {};
470
+ this.timeout = timeout2;
471
+ this._signals = _signals;
472
+ this._setup();
473
+ }
474
+ get abortCtrl() {
475
+ return this.options.abortCtrl;
476
+ }
477
+ get aborted() {
478
+ var _a;
479
+ return this.rejected && !this.timeout.rejected && !!((_a = this._signals) == null ? void 0 : _a.find((s) => s == null ? void 0 : s.aborted));
480
+ }
481
+ cancelAbort() {
482
+ var _a;
483
+ (_a = this._signals) == null ? void 0 : _a.forEach(
484
+ (signal) => signal == null ? void 0 : signal.removeEventListener(
485
+ "abort",
486
+ this._handleAbort
487
+ )
488
+ );
489
+ }
490
+ clearTimeout() {
491
+ clearTimeout(this.timeout.timeoutId);
492
+ }
493
+ get timedout() {
494
+ return this.rejected && this.timeout.rejected;
424
495
  }
425
- timeout2 = isPositiveNumber2(timeout2) && timeout2 || 1e4;
426
- const func = isFn4(PromisEBase_default[funcName]) ? PromisEBase_default[funcName] : PromisEBase_default.all;
427
- const dataPromise = values.length <= 1 ? new PromisEBase_default(values == null ? void 0 : values[0]) : func(values);
428
- const timeoutPromise = delayReject_default(
429
- timeout2,
430
- new Error(timeoutMsg || `Timed out after ${timeout2}ms`)
496
+ };
497
+ TimeoutPromise.defaults = {
498
+ abortOnEarlyFinalize: true,
499
+ batchFunc: "all",
500
+ timeout: TIMEOUT_FALLBACK
501
+ };
502
+ var TimeoutPromise_default = TimeoutPromise;
503
+
504
+ // src/timeout.ts
505
+ import {
506
+ arrUnique as arrUnique2,
507
+ isFn as isFn4,
508
+ isNumber,
509
+ isObj as isObj2,
510
+ isPositiveNumber as isPositiveNumber2,
511
+ objCopy as objCopy3
512
+ } from "@superutils/core";
513
+ function timeout(options, ...values) {
514
+ var _a;
515
+ const opts = objCopy3(
516
+ timeout.defaults,
517
+ isNumber(options) ? { timeout: options } : isObj2(options) ? options : {},
518
+ [],
519
+ "empty"
520
+ );
521
+ opts.timeout = Math.min(
522
+ isPositiveNumber2(opts.timeout) ? opts.timeout : TIMEOUT_FALLBACK,
523
+ TIMEOUT_MAX
524
+ );
525
+ const promises = values.map(
526
+ (v) => isFn4(v) ? PromisEBase_default.try(v) : v
527
+ );
528
+ const dataPromise = promises.length <= 1 ? (
529
+ // single promise resolves to a single result
530
+ promises[0] instanceof PromisEBase_default ? promises[0] : new PromisEBase_default(promises[0])
531
+ ) : (
532
+ // multiple promises resolve to an array of results
533
+ (isFn4(PromisEBase_default[opts.batchFunc]) ? PromisEBase_default[opts.batchFunc] : PromisEBase_default.all)(promises)
534
+ );
535
+ const timeoutPromise = delayReject_default(opts.timeout, opts.onTimeout);
536
+ const promise = new TimeoutPromise_default(
537
+ PromisEBase_default.race([dataPromise, timeoutPromise]),
538
+ timeoutPromise,
539
+ opts,
540
+ arrUnique2(
541
+ [(_a = opts.abortCtrl) == null ? void 0 : _a.signal, opts.signal].filter(Boolean)
542
+ )
431
543
  );
432
- const promise = PromisEBase_default.race([
433
- dataPromise,
434
- timeoutPromise
435
- ]);
436
- promise.clearTimeout = () => clearTimeout(timeoutPromise.timeoutId);
437
- promise.data = dataPromise;
438
- promise.timeout = timeoutPromise;
439
- Object.defineProperty(promise, "timedout", {
440
- get: () => promise.timeout.rejected
441
- });
442
- dataPromise.catch(() => {
443
- }).finally(() => {
444
- promise.clearTimeout();
445
- });
446
544
  return promise;
447
545
  }
448
- timeout.defaultOptions = {
449
- func: "all",
450
- timeout: 1e4
451
- };
452
546
  var timeout_default = timeout;
547
+ timeout.defaults = TimeoutPromise_default.defaults;
548
+ Object.defineProperty(timeout, "defaults", {
549
+ get() {
550
+ return TimeoutPromise_default.defaults;
551
+ },
552
+ set(newDefaults) {
553
+ TimeoutPromise_default.defaults = newDefaults;
554
+ }
555
+ });
453
556
 
454
557
  // src/PromisE.ts
455
558
  var PromisE = class extends PromisEBase_default {
@@ -469,6 +572,9 @@ export {
469
572
  PromisEBase,
470
573
  ResolveError,
471
574
  ResolveIgnored,
575
+ TIMEOUT_FALLBACK,
576
+ TIMEOUT_MAX,
577
+ TimeoutPromise,
472
578
  index_default as default,
473
579
  deferred,
474
580
  deferredCallback,
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.1.6"
7
+ "@superutils/core": "^1.2.1"
8
8
  },
9
9
  "description": "An extended Promise with additional 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.1.6"
26
+ "@superutils/core": "^1.2.0"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
@@ -43,5 +43,6 @@
43
43
  "sideEffects": false,
44
44
  "type": "module",
45
45
  "types": "dist/index.d.ts",
46
- "version": "1.1.6"
46
+ "version": "1.2.1",
47
+ "gitHead": "a26f4b1bb701d99d4cb71443e18680ee3ea52974"
47
48
  }