@superutils/promise 1.1.6 → 1.2.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 (3) hide show
  1. package/dist/index.d.ts +148 -100
  2. package/dist/index.js +117 -44
  3. package/package.json +3 -3
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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 */
@@ -17,63 +17,6 @@ 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 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
20
  type OnEarlyFinalize<T> = <TResolved extends boolean, TValue = TResolved extends true ? T : unknown>(resolved: TResolved, resultOrReason: TValue) => ValueOrPromise<unknown>;
78
21
  type PromiseParams<T = unknown> = ConstructorParameters<typeof Promise<T>>;
79
22
 
@@ -85,7 +28,7 @@ type DeferredAsyncDefaults<ThisArg = unknown, Delay = unknown> = Pick<Required<D
85
28
  delayMs: number;
86
29
  };
87
30
  /** Options for `PromisE.deferred` and other related functions */
88
- type DeferredAsyncOptions<ThisArg = unknown, Delay = unknown> = {
31
+ type DeferredAsyncOptions<ThisArg = unknown, Delay = number> = {
89
32
  /**
90
33
  * Delay in milliseconds, used for `debounce` and `throttle` modes. Use `0` for sequential execution.
91
34
  *
@@ -93,7 +36,7 @@ type DeferredAsyncOptions<ThisArg = unknown, Delay = unknown> = {
93
36
  *
94
37
  * Default: `100` (or whatever is set in `PromisE.deferred.defaults.delayMs`)
95
38
  */
96
- delayMs?: 0 | PositiveNumber<Delay>;
39
+ delayMs?: number | PositiveNumber<Delay>;
97
40
  /**
98
41
  * Whether to ignore (based on `resolveIgnored` settings) stale promises.
99
42
  * In debouce/throttle mode, when an older promise is resolved after a newly resolved promise,
@@ -126,6 +69,8 @@ type DeferredAsyncOptions<ThisArg = unknown, Delay = unknown> = {
126
69
  */
127
70
  resolveError?: ResolveError;
128
71
  } & (({
72
+ delayMs?: PositiveNumber<Delay>;
73
+ } & DeferredOptions<ThisArg>) | ({
129
74
  delayMs: 0;
130
75
  } & {
131
76
  throttle?: false;
@@ -133,9 +78,7 @@ type DeferredAsyncOptions<ThisArg = unknown, Delay = unknown> = {
133
78
  onError?: (this: ThisArg, err: unknown) => ValueOrPromise<unknown>;
134
79
  /** The value to be used as "thisArg" whenever any of the callbacks are invoked */
135
80
  thisArg?: ThisArg;
136
- }) | ({
137
- delayMs?: PositiveNumber<Delay>;
138
- } & DeferredOptions<ThisArg>));
81
+ }));
139
82
  /** Determines what to do when deferred promise/function fails */
140
83
  declare enum ResolveError {
141
84
  /** Neither resolve nor reject the failed */
@@ -160,6 +103,51 @@ declare enum ResolveIgnored {
160
103
  WITH_UNDEFINED = "WITH_UNDEFINED"
161
104
  }
162
105
 
106
+ interface IPromisE_Delay<T = unknown> extends Promise<T>, IPromisE<T> {
107
+ /**
108
+ * Caution: pausing will prevent the promise from resolving/rejeting automatically.
109
+ *
110
+ * In order to finalize the promise either the `resolve()` or the `reject()` method must be invoked manually.
111
+ *
112
+ * An never-finalized promise may cause memory leak and will leave it at the mercry of the garbage collector.
113
+ * Use `pause()` only if you are sure.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * // Example 1: SAFE => no memory leak, because no reference to the promise is stored and no suspended code
118
+ * <button onClick={() => {
119
+ * const promise = PromisE.delay(1000).then(... do stuff ....)
120
+ * setTimeout(() => promise.pause(), 300)
121
+ * }}>Click Me</button>
122
+ * ```
123
+ *
124
+ * @example UNSAFE => potential memory leak, because of suspended code
125
+ * ```typescript
126
+ * <button onClick={() => {
127
+ * const promise = PromisE.delay(1000)
128
+ * setTimeout(() => promise.pause(), 300)
129
+ * await promise // suspended code
130
+ * //... do stuff ....
131
+ * }}>Click Me</button>
132
+ * ```
133
+ *
134
+ * @example UNSAFE => potential memory leak, because of preserved reference.
135
+ * ```typescript
136
+ * // Until the reference to promises is collected by the garbage collector,
137
+ * // reference to the unfinished promise will remain in memory.
138
+ * const promises = []
139
+ * <button onClick={() => {
140
+ * const promise = PromisE.delay(1000)
141
+ * setTimeout(() => promise.pause(), 300)
142
+ * promises.push(promise)
143
+ * }}>Click Me</button>
144
+ * ```
145
+ */
146
+ pause: () => void;
147
+ /** Timeout ID */
148
+ timeoutId: TimeoutId;
149
+ }
150
+
163
151
  /** Function to determine whether retry should be attempted based on previous result/error */
164
152
  type RetryIfFunc<T = unknown> = (prevResult: T | undefined, retryCount: number, error?: unknown) => ValueOrPromise<boolean | void>;
165
153
  /** Options for automatic retry mechanism */
@@ -273,7 +261,7 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
273
261
  /** Sugar for `new PromisE(Promise.race(..))` */
274
262
  static race: <T_1 extends unknown[]>(values: T_1) => IPromisE<Awaited<T_1[number]>>;
275
263
  /** Extends Promise.reject */
276
- static reject: <T_1 = never>(reason: unknown) => IPromisE<T_1>;
264
+ static reject: <T_1 = never>(reason: unknown) => PromisEBase<T_1>;
277
265
  /** Sugar for `new PromisE(Promise.resolve(...))` */
278
266
  static resolve: <T_1>(value?: T_1 | PromiseLike<T_1>) => IPromisE<T_1>;
279
267
  /** Sugar for `new PromisE(Promise.try(...))` */
@@ -310,25 +298,62 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
310
298
  };
311
299
  }
312
300
 
313
- type TimeoutFunc<T extends unknown[]> = {
301
+ /**
302
+ * Descibes a timeout PromisE and it's additional properties.
303
+ */
304
+ type IPromisE_Timeout<T = unknown> = IPromisE<T> & {
305
+ readonly aborted: boolean;
306
+ /**
307
+ * Removes `abortCtrl/signal` listeners, effectively disabling external cancellation via AbortController.
308
+ */
309
+ cancelAbort: () => void;
310
+ /**
311
+ * Clears the timeout timer, preventing the promise from being rejected due to a timeout.
312
+ */
313
+ clearTimeout: () => void;
314
+ /** The underlying data promise. If multiple promises were passed to `timeout`, this represents the combined result (defaulting to `PromisE.all`). */
315
+ data: IPromisE<T>;
316
+ /** Read-only property indicating if the promise timed out. Equivalent to checking `promise.timeout.rejected`. */
317
+ readonly timedout: boolean;
318
+ /** The internal promise that handles the timeout logic. It rejects when the duration expires. */
319
+ timeout: IPromisE_Delay<T>;
320
+ };
321
+ type TimeoutResult<T extends unknown[], TFunc extends keyof TimeoutFunc<T>, Values extends unknown[] = {
322
+ -readonly [P in keyof T]: T[P] extends (...args: unknown[]) => infer ReturnType ? ReturnType : T[P];
323
+ }> = Awaited<T['length'] extends 1 ? Values[0] : ReturnType<TimeoutFunc<Values>[TFunc]>>;
324
+ type TimeoutFunc<T extends unknown[] = []> = {
314
325
  all: typeof PromisEBase.all<T>;
315
326
  allSettled: typeof PromisEBase.allSettled<T>;
316
327
  any: typeof PromisEBase.any<T>;
317
328
  race: typeof PromisEBase.race<T>;
318
329
  };
319
330
  /**
320
- * `PromisE.timeout` options
331
+ * Options for `PromisE.timeout()`
321
332
  *
322
- * @param func (optional) name of the supported `PromieBase` static method. Default: `"all"`
333
+ * @param func (optional) name of the supported `PromiEBase` static method to be used to resolve
334
+ * when more than one promise/function is provided. Default: `"all"`
323
335
  * @param timeout (optional) timeout duration in milliseconds. Default: `10_000` (10 seconds)
324
336
  * @param timeoutMsg (optional) timeout error message. Default: `"Timed out after 10000ms"`
325
337
  *
326
338
  */
327
- type TimeoutOptions<Func extends string = 'all'> = {
328
- func: Func;
339
+ type TimeoutOptions<T extends unknown[] = [], Func extends string = 'all'> = {
340
+ abortCtrl?: AbortController;
341
+ func?: T['length'] extends 0 ? never : T['length'] extends 1 ? never : Func;
342
+ /**
343
+ * Callback invoked when the promise is rejected due to an abort signal.
344
+ * Optionally, return an `Error` object to reject the promise with a custom error.
345
+ */
346
+ onAbort?: () => ValueOrPromise<void | Error>;
347
+ /**
348
+ * Callback invoked when the promise times out.
349
+ * Optionally, return an `Error` object to reject the promise with a custom error.
350
+ */
351
+ onTimeout?: () => ValueOrPromise<void | Error>;
352
+ signal?: AbortSignal;
329
353
  timeout?: number;
330
- timeoutMsg?: string;
331
354
  };
355
+ /** Default options for `PromisE.timeout()` */
356
+ type TimeoutOptionsDefault = Required<Omit<TimeoutOptions<unknown[], keyof TimeoutFunc>, 'abortCtrl' | 'signal'>>;
332
357
 
333
358
  /**
334
359
  * @function PromisE.deferred
@@ -521,7 +546,7 @@ declare function deferredCallback<TDefault = unknown, ThisArg = unknown, Delay =
521
546
  * func()
522
547
  * ```
523
548
  */
524
- declare function delay<T = number, TReject extends boolean = boolean>(duration?: number, result?: T | (() => T), asRejected?: TReject): IPromisE_Delay<T>;
549
+ declare function delay<T = number, TReject extends boolean = boolean>(duration?: number, result?: T | (() => T | Promise<T>), asRejected?: TReject): IPromisE_Delay<T>;
525
550
  declare namespace delay {
526
551
  var defaults: {
527
552
  /** Default delay duration in milliseconds */
@@ -565,18 +590,22 @@ declare namespace delay {
565
590
  */
566
591
  declare function delayReject<T = never>(duration: number, reason?: unknown): IPromisE_Delay<T>;
567
592
 
593
+ /** Timeout duration (in milliseconds) used as a fallback when positive number is not provided to {@link timeout} */
594
+ declare const FALLBACK_TIMEOUT = 10000;
568
595
  /**
569
596
  * Creates a new promise that wraps one or more promises and rejects if they do not settle within a
570
597
  * specified timeout duration. When multiple promises are provided, they can be processed using methods like
571
598
  * `all` (default), `race`, `any`, or `allSettled`.
572
599
  *
573
600
  * @param timeout (optional) timeout duration in milliseconds.
574
- * Default: `10000` (10 seconds)
601
+ * Default: `10_000` (10 seconds)
575
602
  *
576
603
  * @param values rest param containing one or more promises/values
577
604
  *
578
- * @example Wokring with a single promise: resolved succesfully
605
+ * @example Working with a single promise
579
606
  * ```typescript
607
+ * import PromisE from '@supertuils/promise'
608
+ *
580
609
  * PromisE.timeout(
581
610
  * 5000, // timeout after 5000ms
582
611
  * PromisE.delay(1000), // resolves after 1000ms with value 1000
@@ -584,8 +613,21 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
584
613
  * // Result: 1000
585
614
  * ```
586
615
  *
616
+ * @example Working with a single function
617
+ * ```typescript
618
+ * import PromisE from '@supertuils/promise'
619
+ *
620
+ * PromisE.timeout(
621
+ * 5000, // timeout after 5000ms
622
+ * () => PromisE.delay(1000), // function resolves after 1000ms with value 1000
623
+ * ).then(console.log)
624
+ * // Result: 1000
625
+ * ```
626
+ *
587
627
  * @example Promise times out & rejected
588
628
  * ```typescript
629
+ * import PromisE from '@supertuils/promise'
630
+ *
589
631
  * PromisE.timeout(
590
632
  * 5000, // timeout after 5000ms
591
633
  * PromisE.delay(20000), // resolves after 20000ms with value 20000
@@ -593,13 +635,15 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
593
635
  * // Error: Error('Timed out after 5000ms')
594
636
  *```
595
637
  *
596
- * @example Working with multiple promises, resolved using "PromisE.all()"
638
+ * @example Working with multiple promises/functions, resolved using "PromisE.all()"
597
639
  *
598
640
  * ```typescript
641
+ * import PromisE from '@supertuils/promise'
642
+ *
599
643
  * PromisE.timeout(
600
644
  * 5000, // timeout after 5000ms
601
645
  * PromisE.delay(1000), // resolves after 1000ms with value 1000
602
- * PromisE.delay(2000), // resolves after 2000ms with value 2000
646
+ * () => PromisE.delay(2000), // resolves after 2000ms with value 2000
603
647
  * PromisE.delay(3000), // resolves after 3000ms with value 3000
604
648
  * ).then(console.log)
605
649
  * // Result: [ 1000, 2000, 3000 ]
@@ -608,6 +652,8 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
608
652
  * @example Promise times out & but not rejected.
609
653
  * Eg: when API request is taking longer than expected, print a message avoid rejecting the promise.
610
654
  * ```typescript
655
+ * import PromisE from '@supertuils/promise'
656
+ *
611
657
  * const promise = PromisE.timeout(
612
658
  * 5000, // timeout after 5000ms
613
659
  * PromisE.delay(20000), // data promise, resolves after 20000ms with value 20000
@@ -622,10 +668,32 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
622
668
  * return promise.data
623
669
  * })
624
670
  *```
671
+ */
672
+ declare function timeout<T extends [unknown, ...unknown[]]>(timeout: number, ...values: T): IPromisE_Timeout<TimeoutResult<T, 'all'>>;
673
+ /**
674
+ *
675
+ * @param options An options object can be passed with one or more of the following properties:
676
+ * @param options.abortCtrl (optional) AbortController to manually reject promise externally and/or to sync abort with timeout rejection
677
+ * @param options.abortMsg (optional) error message when promise is rejected by abort controller/signal
678
+ * @param options.func (optional) Name of the `PromisE` static method to be used to combine the `values`.
679
+ * Only used when more than one promise is provided. Default: `"all"`
680
+ *
681
+ * Accepted values:
682
+ * 1. `'all'` **(default)**: for `PromisE.all`
683
+ * 2. `'allSettled'`: for `PromisE.allSettled`
684
+ * 3. `'any'`: for `PromisE.any`
685
+ * 4. `'race'`: for `PromisE.race`
686
+ * @param options.signal (optional) AbortSignal to manually reject promise externally
687
+ * @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)
688
+ * @param options.timeoutMsg (optional) custom error message to be used when promises timeout.
689
+ *
690
+ * @param values Mix of promises, values and/or functions
625
691
  *
626
- * @example Multiple promises resolved using "PromisE.race()"
692
+ * @example Working with multiple promises/functions resolved using "PromisE.race()"
627
693
  *
628
694
  * ```typescript
695
+ * import PromisE from '@supertuils/promise'
696
+ *
629
697
  * PromisE.timeout(
630
698
  * { // instead of `timeout: number` an object can be used for additional options
631
699
  * func: 'race', // tells PromisE.timeout to use `PromisE.race(promises)`
@@ -633,35 +701,15 @@ declare function delayReject<T = never>(duration: number, reason?: unknown): IPr
633
701
  * timeoutMsg: 'My custom timed out message',
634
702
  * },
635
703
  * PromisE.delay(1000), // resolves after 1000ms with value 1000
636
- * PromisE.delay(2000), // resolves after 2000ms with value 2000
704
+ * () => PromisE.delay(2000), // resolves after 2000ms with value 2000
637
705
  * PromisE.delay(3000), // resolves after 3000ms with value 3000
638
706
  * ).then(console.log)
639
707
  * // Result: 1000 (Result of `Promise.race(promises)`)
640
708
  * ```
641
709
  */
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>;
710
+ declare function timeout<T extends unknown[], TFunc extends keyof TimeoutFunc<T> = keyof TimeoutFunc<T>>(options: TimeoutOptions<T, TFunc>, ...values: T): IPromisE_Timeout<TimeoutResult<T, TFunc>>;
663
711
  declare namespace timeout {
664
- var defaultOptions: Required<TimeoutOptions>;
712
+ var defaults: Required<Omit<TimeoutOptions<unknown[], keyof TimeoutFunc<[]>>, "abortCtrl" | "signal">>;
665
713
  }
666
714
 
667
715
  /**
@@ -792,4 +840,4 @@ declare const retry: {
792
840
  };
793
841
  };
794
842
 
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 };
843
+ export { type DeferredAsyncCallback, type DeferredAsyncDefaults, type DeferredAsyncOptions, FALLBACK_TIMEOUT, 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, type TimeoutOptionsDefault, type TimeoutResult, PromisE as default, deferred, deferredCallback, delay, delayReject, retry, timeout };
package/dist/index.js CHANGED
@@ -87,11 +87,6 @@ var _PromisEBase = class _PromisEBase extends Promise {
87
87
  get state() {
88
88
  return this._state;
89
89
  }
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
90
  };
96
91
  //
97
92
  //
@@ -108,8 +103,8 @@ _PromisEBase.any = (values) => new _PromisEBase(globalThis.Promise.any(values));
108
103
  _PromisEBase.race = (values) => new _PromisEBase(globalThis.Promise.race(values));
109
104
  /** Extends Promise.reject */
110
105
  _PromisEBase.reject = (reason) => {
111
- const { promise, reject } = _PromisEBase.withResolvers();
112
- queueMicrotask(() => reject(reason));
106
+ const promise = new _PromisEBase();
107
+ queueMicrotask(() => promise.reject(reason));
113
108
  return promise;
114
109
  };
115
110
  /** Sugar for `new PromisE(Promise.resolve(...))` */
@@ -312,23 +307,28 @@ var deferredCallback_default = deferredCallback;
312
307
 
313
308
  // src/delay.ts
314
309
  import { fallbackIfFails as fallbackIfFails3, isFn as isFn3 } from "@superutils/core";
315
- function delay(duration = delay.defaults.duration, result = duration, asRejected = false) {
310
+ function delay(duration = delay.defaults.duration, result, asRejected = false) {
316
311
  const promise = new PromisEBase_default();
317
312
  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
- )
313
+ const _result = fallbackIfFails3(
314
+ async () => {
315
+ const _result2 = await (isFn3(result2) ? result2() : result2);
316
+ return !asRejected ? _result2 != null ? _result2 : duration : _result2 != null ? _result2 : new Error(
317
+ `${delay.defaults.delayTimeoutMsg} ${duration}ms`
318
+ );
319
+ },
320
+ [],
321
+ // when result is a function and it fails/rejects,
322
+ // promise will reject even if `asRejected = false`
323
+ (err) => Promise.reject(err)
326
324
  );
325
+ !asRejected ? promise.resolve(_result) : _result.then(promise.reject, promise.reject);
327
326
  };
328
327
  promise.timeoutId = setTimeout(() => finalize(result), duration);
329
328
  promise.pause = () => clearTimeout(promise.timeoutId);
330
329
  promise.catch(() => {
331
330
  }).finally(() => promise.pause());
331
+ promise.onEarlyFinalize.push(() => promise.pause());
332
332
  return promise;
333
333
  }
334
334
  delay.defaults = {
@@ -412,44 +412,116 @@ retry.defaults = {
412
412
  var retry_default = retry;
413
413
 
414
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;
424
- }
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`)
415
+ import {
416
+ arrUnique,
417
+ fallbackIfFails as fallbackIfFails5,
418
+ isFn as isFn4,
419
+ isObj,
420
+ isPositiveNumber as isPositiveNumber2,
421
+ noop,
422
+ objCopy as objCopy3
423
+ } from "@superutils/core";
424
+ var FALLBACK_TIMEOUT = 1e4;
425
+ function timeout(timeoutOrOptions, ...values) {
426
+ const options = objCopy3(
427
+ timeout.defaults,
428
+ isObj(timeoutOrOptions) ? timeoutOrOptions : { timeout: timeoutOrOptions },
429
+ [],
430
+ "empty"
431
+ );
432
+ const { func, onTimeout } = options;
433
+ const duration = isPositiveNumber2(options.timeout) ? options.timeout : FALLBACK_TIMEOUT;
434
+ const arrPromises = values.map((v) => isFn4(v) ? PromisEBase_default.try(v) : v);
435
+ const dataPromise = arrPromises.length <= 1 ? (
436
+ // single promise resolves to a single result
437
+ arrPromises[0] instanceof PromisEBase_default ? arrPromises[0] : new PromisEBase_default(arrPromises[0])
438
+ ) : (
439
+ // multiple promises resolve to an array of results
440
+ (isFn4(PromisEBase_default[func]) ? PromisEBase_default[func] : PromisEBase_default.all)(
441
+ arrPromises
442
+ )
431
443
  );
444
+ const timeoutPromise = delayReject_default(duration, onTimeout);
432
445
  const promise = PromisEBase_default.race([
433
446
  dataPromise,
434
447
  timeoutPromise
435
448
  ]);
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
- });
449
+ addPropsNListeners(promise, dataPromise, timeoutPromise, options);
446
450
  return promise;
447
451
  }
448
- timeout.defaultOptions = {
452
+ timeout.defaults = {
449
453
  func: "all",
450
- timeout: 1e4
454
+ timeout: FALLBACK_TIMEOUT
451
455
  };
452
456
  var timeout_default = timeout;
457
+ var addPropsNListeners = (promise, dataPromise, timeoutPromise, options) => {
458
+ const { abortCtrl, onAbort, signal } = options;
459
+ const signals = arrUnique([abortCtrl == null ? void 0 : abortCtrl.signal, signal].filter(Boolean));
460
+ Object.defineProperties(promise, {
461
+ aborted: {
462
+ get() {
463
+ return promise.rejected && !!signals.find((s) => s.aborted);
464
+ }
465
+ },
466
+ cancelAbort: {
467
+ get() {
468
+ return () => {
469
+ signals == null ? void 0 : signals.forEach(
470
+ (signal2) => signal2.removeEventListener("abort", handleAbort)
471
+ );
472
+ };
473
+ }
474
+ },
475
+ clearTimeout: {
476
+ get() {
477
+ return () => clearTimeout(timeoutPromise.timeoutId);
478
+ }
479
+ },
480
+ data: {
481
+ get() {
482
+ return dataPromise;
483
+ }
484
+ },
485
+ timeout: {
486
+ get() {
487
+ return timeoutPromise;
488
+ }
489
+ },
490
+ timedout: {
491
+ get() {
492
+ return promise.rejected && timeoutPromise.rejected;
493
+ }
494
+ }
495
+ });
496
+ const cleanup = () => {
497
+ promise.cancelAbort();
498
+ promise.clearTimeout();
499
+ };
500
+ promise.onEarlyFinalize.push(cleanup);
501
+ promise.catch(() => {
502
+ var _a;
503
+ if (!timeoutPromise.rejected && !signals.find((x) => x.aborted))
504
+ return;
505
+ ((_a = abortCtrl == null ? void 0 : abortCtrl.signal) == null ? void 0 : _a.aborted) === false && abortCtrl.abort();
506
+ }).finally(cleanup);
507
+ if (!signals.length) return;
508
+ const started = /* @__PURE__ */ new Date();
509
+ function handleAbort() {
510
+ if (!promise.pending) return;
511
+ fallbackIfFails5(async () => await (onAbort == null ? void 0 : onAbort()), [], void 0).then(
512
+ (err) => {
513
+ var _a;
514
+ err != null ? err : err = new Error(
515
+ `Aborted after ${(/* @__PURE__ */ new Date()).getTime() - started.getTime()}ms`
516
+ );
517
+ (_a = err.name) != null ? _a : err.name = "AbortError";
518
+ promise.reject(err);
519
+ },
520
+ noop
521
+ );
522
+ }
523
+ signals.forEach((signal2) => signal2.addEventListener("abort", handleAbort));
524
+ };
453
525
 
454
526
  // src/PromisE.ts
455
527
  var PromisE = class extends PromisEBase_default {
@@ -465,6 +537,7 @@ var PromisE_default = PromisE;
465
537
  // src/index.ts
466
538
  var index_default = PromisE_default;
467
539
  export {
540
+ FALLBACK_TIMEOUT,
468
541
  PromisE,
469
542
  PromisEBase,
470
543
  ResolveError,
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.1.8"
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,5 @@
43
43
  "sideEffects": false,
44
44
  "type": "module",
45
45
  "types": "dist/index.d.ts",
46
- "version": "1.1.6"
46
+ "version": "1.2.0"
47
47
  }