@superutils/promise 1.0.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.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/index.d.ts +617 -0
- package/dist/index.js +408 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Toufiqur Rahaman Chowdhury
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# @superutils/promise
|
|
2
|
+
|
|
3
|
+
An extended `Promise` implementation, named `PromisE`, that provides additional features and utilities for easier asynchronous flow control in JavaScript and TypeScript applications.
|
|
4
|
+
|
|
5
|
+
This package offers a drop-in replacement for the native `Promise` that includes status tracking (`.pending`, `.resolved`, `.rejected`) and a suite of powerful static methods for common asynchronous patterns like deferred execution, throttling, and cancellable fetches.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Usage](#usage)
|
|
12
|
+
- [`new PromisE(executor)`](#promise-executor): Drop-in replacement for `Promise`
|
|
13
|
+
- [`new PromisE(promise)`](#promise-status): Check promise status
|
|
14
|
+
- [`PromisE.try()`](#static-methods): Static methods
|
|
15
|
+
- [`PromisE.delay()`](#delay): Async delay
|
|
16
|
+
- [`PromisE.deferred()`](#deferred): Async debounced/throttled callback
|
|
17
|
+
- [`PromisE.timeout()`](#timeout): Reject after timeout
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Promise Status**: Easily check if a promise is `pending`, `resolved`, or `rejected`.
|
|
22
|
+
- **Deferred Execution**: Defer or throttle promise-based function calls with `PromisE.deferred()`.
|
|
23
|
+
- **Auto-cancellable Fetch**: Automatically abort pending requests when subsequent requests are made using `PromisE.deferredFetch()` and `PromisE.deferredPost()`.
|
|
24
|
+
- **Auto-cancellable Fetch**: The `PromisE.deferredFetch` and `PromisE.deferredPost` utilities automatically abort pending requests when a new deferred/throttled call is made.
|
|
25
|
+
- **Timeouts**: Wrap any promise with a timeout using `PromisE.timeout()`.
|
|
26
|
+
- **Rich Utilities**: A collection of static methods like `.all()`, `.race()`, `.delay()`, and more, all returning `PromisE` instances.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @superutils/core @superutils/promise
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
<div id="promise-executor"></div>
|
|
37
|
+
|
|
38
|
+
### `new PromisE(executor)`: Drop-in replacement for `Promise`
|
|
39
|
+
|
|
40
|
+
The `PromisE` class can be used just like the native `Promise`. The key difference is the addition of status properties:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { PromisE } from '@superutils/promise'
|
|
44
|
+
|
|
45
|
+
const p = new PromisE(resolve => setTimeout(() => resolve('done'), 1000))
|
|
46
|
+
|
|
47
|
+
console.log(p.pending) // true
|
|
48
|
+
|
|
49
|
+
p.then(result => {
|
|
50
|
+
console.log(result) // 'done'
|
|
51
|
+
console.log(p.resolved) // true
|
|
52
|
+
console.log(p.pending) // false
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
and the ability to early finalize a promise:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { PromisE } from '@superutils/promise'
|
|
60
|
+
const p = new PromisE(resolve => setTimeout(() => resolve('done'), 10000))
|
|
61
|
+
p.then(result => console.log(result))
|
|
62
|
+
// resolve the promise early
|
|
63
|
+
setTimeout(() => p.resolve('finished early'), 500)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
<div id="static-methods"></div>
|
|
67
|
+
|
|
68
|
+
### `PromisE.try(fn)`: Static methods
|
|
69
|
+
|
|
70
|
+
Drop-in replacement for all `Promise` static methods such as `.all()`, `.race()`, `.reject`, `.resolve`, `.try()`, `.withResolvers()`....
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { PromisE } from '@superutils/promise'
|
|
74
|
+
|
|
75
|
+
const p = PromisE.try(() => {
|
|
76
|
+
throw new Error('Something went wrong')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
p.catch(error => {
|
|
80
|
+
console.error(error.message) // 'Something went wrong'
|
|
81
|
+
console.log(p.rejected) // true
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
<div id="promise-status"></div>
|
|
86
|
+
|
|
87
|
+
### `new PromisE(promise)`
|
|
88
|
+
|
|
89
|
+
Check status of an existing promise.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { PromisE } from '@superutils/promise'
|
|
93
|
+
const x = Promise.resolve(1)
|
|
94
|
+
const p = new PromisE(x)
|
|
95
|
+
console.log(p.pending) // false
|
|
96
|
+
console.log(p.resolved) // true
|
|
97
|
+
console.log(p.rejected) // false
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
<div id="delay"></div>
|
|
101
|
+
|
|
102
|
+
### `PromisE.delay(duration)`: Async delay
|
|
103
|
+
|
|
104
|
+
Creates a promise that resolves after a specified duration, essentially a promise-based `setTimeout`.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import PromisE from '@superutils/promise'
|
|
108
|
+
// Wait until `appReady` becomes truthy but
|
|
109
|
+
while (!appReady) {
|
|
110
|
+
await PromisE.delay(100)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `PromisE.delay(duration, callback)`: execute after delay
|
|
115
|
+
|
|
116
|
+
Creates a promise that executes a function after a specified duration and returns the value the function returns.
|
|
117
|
+
|
|
118
|
+
If callback returns undefined, default value will be the duration.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import PromisE from '@superutils/promise'
|
|
122
|
+
|
|
123
|
+
const callback = () => {
|
|
124
|
+
/* do stuff here */
|
|
125
|
+
}
|
|
126
|
+
await PromisE.delay(100, callback)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
<div id="deferred"></div>
|
|
130
|
+
|
|
131
|
+
### `PromisE.deferred(options)`: async debounced/throttled execution
|
|
132
|
+
|
|
133
|
+
Create a function that debounces or throttles promise-returning function calls. This is useful for scenarios like auto-saving user input or preventing multiple rapid API calls.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import PromisE, { ResolveIgnored } from '@superutils/promise'
|
|
137
|
+
|
|
138
|
+
// Create a deferred function that waits 300ms after the last call
|
|
139
|
+
const deferredSave = PromisE.deferred({
|
|
140
|
+
defer: 300,
|
|
141
|
+
/** ignored promises will resolve with `undefined` */
|
|
142
|
+
resolveIgnored: ResolveIgnored.WITH_UNDEFINED,
|
|
143
|
+
|
|
144
|
+
/** ignored promises will NEVER be resolved/rejected
|
|
145
|
+
* USE WITH CAUTION!
|
|
146
|
+
*/
|
|
147
|
+
resolveIgnored: ResolveIgnored.NEVER,
|
|
148
|
+
|
|
149
|
+
// ignored promises will resolve with the result of the last call
|
|
150
|
+
resolveIgnored: ResolveIgnored.WITH_LAST, // (default)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// Simulate rapid calls
|
|
154
|
+
deferredSave(() => api.save({ text: 'first' }))
|
|
155
|
+
deferredSave(() => api.save({ text: 'second' }))
|
|
156
|
+
// Only the 3rd call is executed.
|
|
157
|
+
// But all of them are resolved with the result of the 3rd call when `resolveIgnored` is `ResolveIgnored.WITH_LAST`
|
|
158
|
+
deferredSave(() => api.save({ text: 'third' })).then(response =>
|
|
159
|
+
console.log('Saved!', response),
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
<div id="deferredCallback"></div>
|
|
164
|
+
|
|
165
|
+
### `PromisE.deferredCallback(callback, options)`: async debounced/throttled callbacks
|
|
166
|
+
|
|
167
|
+
Same as `PromisE.deferred` but for event handlers etc.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import PromisE from '@superutils/promise'
|
|
171
|
+
|
|
172
|
+
// Input change handler
|
|
173
|
+
const handleChange = (e: { target: { value: number } }) =>
|
|
174
|
+
console.log(e.target.value)
|
|
175
|
+
// Change handler with `PromisE.deferred()`
|
|
176
|
+
const handleChangeDeferred = PromisE.deferredCallback(handleChange, {
|
|
177
|
+
delayMs: 300,
|
|
178
|
+
throttle: false,
|
|
179
|
+
})
|
|
180
|
+
// Simulate input change events after prespecified delays
|
|
181
|
+
const delays = [100, 150, 200, 550, 580, 600, 1000, 1100]
|
|
182
|
+
delays.forEach(timeout =>
|
|
183
|
+
setTimeout(
|
|
184
|
+
() => handleChangeDeferred({ target: { value: timeout } }),
|
|
185
|
+
timeout,
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
// Prints:
|
|
189
|
+
// 200, 600, 1100
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
<div id="timeout"></div>
|
|
193
|
+
|
|
194
|
+
### `PromisE.timeout(duration, ...promises)`: Reject after timeout
|
|
195
|
+
|
|
196
|
+
#### Reject stuck or unexpectedly lenghthy promise(s) after a specified timeout:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { PromisE } from '@superutils/promise'
|
|
200
|
+
|
|
201
|
+
PromisE.timeout(
|
|
202
|
+
5000, // timeout after 5000ms
|
|
203
|
+
api.save({ text: 'takes longer than 5s to finish' }),
|
|
204
|
+
).catch(console.log)
|
|
205
|
+
// Error: Error('Timed out after 5000ms')
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Show a message when loading is too long:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { PromisE } from '@superutils/promise'
|
|
212
|
+
|
|
213
|
+
const loadUserNProducts = () => {
|
|
214
|
+
const promise = PromisE.timeout(
|
|
215
|
+
5000, // timeout after 5000ms
|
|
216
|
+
api.getUser(),
|
|
217
|
+
api.getProducts(),
|
|
218
|
+
)
|
|
219
|
+
const [user, products] = await promise.catch(err => {
|
|
220
|
+
// promise did not time out, but was rejected
|
|
221
|
+
// because one of the data promises rejected
|
|
222
|
+
if (!promise.timedout) return Promise.reject(err)
|
|
223
|
+
|
|
224
|
+
// promise timed out >> print/update UI
|
|
225
|
+
console.log('Request is taking longer than expected......')
|
|
226
|
+
// now return the "data promise", the promise(s) provided in the PromisE.timeout()
|
|
227
|
+
// If more than one promises provided, then `promise.data` will be the combination of them all: `PromisE.all(...promises)`
|
|
228
|
+
return promise.data
|
|
229
|
+
})
|
|
230
|
+
return [user, products]
|
|
231
|
+
}
|
|
232
|
+
loadUserNProducts()
|
|
233
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
import * as _superutils_core from '@superutils/core';
|
|
2
|
+
import { ValueOrPromise, TimeoutId, ThrottleConfig, DeferredConfig } from '@superutils/core';
|
|
3
|
+
|
|
4
|
+
interface IPromisE<T = unknown> extends Promise<T> {
|
|
5
|
+
/** 0: pending, 1: resolved, 2: rejected */
|
|
6
|
+
readonly state: 0 | 1 | 2;
|
|
7
|
+
/** callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve/reject methods */
|
|
8
|
+
onEarlyFinalize: OnEarlyFinalize<T>[];
|
|
9
|
+
/** Indicates if the promise is still pending/unfinalized */
|
|
10
|
+
readonly pending: boolean;
|
|
11
|
+
/** Reject pending promise early. */
|
|
12
|
+
reject: (reason: unknown) => void;
|
|
13
|
+
/** Indicates if the promise has been rejected */
|
|
14
|
+
readonly rejected: boolean;
|
|
15
|
+
/** Resovle pending promise early. */
|
|
16
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
17
|
+
/** Indicates if the promise has been resolved */
|
|
18
|
+
readonly resolved: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface IPromisE_Delay<T = unknown> extends 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
|
+
timeoutId: TimeoutId;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Descibes a timeout PromisE and it's additional properties.
|
|
65
|
+
*/
|
|
66
|
+
type IPromisE_Timeout<T = unknown> = IPromisE<T> & {
|
|
67
|
+
/** Clearing the timeout will prevent it from timing out */
|
|
68
|
+
clearTimeout: () => void;
|
|
69
|
+
/** The result/data promise. If more than one supplied in `args` result promise will be a combined `PromisE.all` */
|
|
70
|
+
data: IPromisE<T>;
|
|
71
|
+
/** A shorthand getter to check if the promise has timed out. Same as `promise.timeout.rejected`. */
|
|
72
|
+
readonly timedout: boolean;
|
|
73
|
+
/** The timeout promise */
|
|
74
|
+
timeout: IPromisE_Delay<T>;
|
|
75
|
+
};
|
|
76
|
+
type OnEarlyFinalize<T> = <TResolved extends boolean, TValue = TResolved extends true ? T : unknown>(resolved: TResolved, resultOrReason: TValue) => ValueOrPromise<unknown>;
|
|
77
|
+
type PromiseParams<T = unknown> = ConstructorParameters<typeof Promise<T>>;
|
|
78
|
+
|
|
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;
|
|
84
|
+
/** Callback invoked whenever promise/function throws error */
|
|
85
|
+
onError?: (err: unknown) => ValueOrPromise<unknown>;
|
|
86
|
+
/**
|
|
87
|
+
* Whenever a promise/function is ignored when in debource/throttle mode, `onIgnored` wil be invoked.
|
|
88
|
+
* The promise/function will not be invoked, unless it's manually invoked using the `ignored` function.
|
|
89
|
+
* Use for debugging or logging purposes.
|
|
90
|
+
*/
|
|
91
|
+
onIgnore?: (ignored: () => Promise<unknown>) => ValueOrPromise<unknown>;
|
|
92
|
+
/**
|
|
93
|
+
* Whenever a promise/function is executed successfully `onResult` will be called.
|
|
94
|
+
* Those that are ignored but resolve with last will not cause `onResult` to be invoked.
|
|
95
|
+
*
|
|
96
|
+
* Result can be `undefined` if `ResolveIgnored.WITH_UNDEFINED` is used.
|
|
97
|
+
*/
|
|
98
|
+
onResult?: (result?: unknown) => ValueOrPromise<unknown>;
|
|
99
|
+
/**
|
|
100
|
+
* Indicates what to do when a promise in the queue is ignored.
|
|
101
|
+
* See {@link ResolveIgnored} for available options.
|
|
102
|
+
*/
|
|
103
|
+
resolveIgnored?: ResolveIgnored;
|
|
104
|
+
resolveError?: ResolveError;
|
|
105
|
+
/** Enable throttle mode. Requires {@link DeferredOptions.delayMs}*/
|
|
106
|
+
throttle?: boolean;
|
|
107
|
+
} & (({
|
|
108
|
+
delayMs: number;
|
|
109
|
+
throttle: true;
|
|
110
|
+
} & ThrottleConfig<ThisArg>) | ({
|
|
111
|
+
delayMs?: number;
|
|
112
|
+
throttle?: false;
|
|
113
|
+
} & DeferredConfig<ThisArg>));
|
|
114
|
+
/** Options for what to do when deferred promise/function fails */
|
|
115
|
+
declare enum ResolveError {
|
|
116
|
+
/** Neither resolve nor reject the failed */
|
|
117
|
+
NEVER = "NEVER",
|
|
118
|
+
/** (default) Reject the failed as usual */
|
|
119
|
+
REJECT = "REJECT",
|
|
120
|
+
/** Resolve (not reject) with the error/reason */
|
|
121
|
+
WITH_ERROR = "RESOLVE_ERROR",
|
|
122
|
+
/** Resolve with undefined */
|
|
123
|
+
WITH_UNDEFINED = "RESOLVE_UNDEFINED"
|
|
124
|
+
}
|
|
125
|
+
/** Options for what to do when a promise/callback is ignored, either because of being deferred, throttled or another been prioritized. */
|
|
126
|
+
declare enum ResolveIgnored {
|
|
127
|
+
/** Never resolve ignored promises. Caution: make sure this doesn't cause any memory leaks. */
|
|
128
|
+
NEVER = "NEVER",
|
|
129
|
+
/** (default) resolve with active promise result, the one that caused the current promise/callback to be ignored). */
|
|
130
|
+
WITH_LAST = "WITH_LAST",
|
|
131
|
+
/** resolve with `undefined` value */
|
|
132
|
+
WITH_UNDEFINED = "WITH_UNDEFINED"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Global configuration */
|
|
136
|
+
declare const config: {
|
|
137
|
+
/** Default value for `options` used by `PromisE.*deferred*` functions */
|
|
138
|
+
deferOptions: DeferredOptions;
|
|
139
|
+
delayTimeoutMsg: string;
|
|
140
|
+
retryOptions: {
|
|
141
|
+
retry: number;
|
|
142
|
+
retryBackOff: "exponential";
|
|
143
|
+
retryDelay: number;
|
|
144
|
+
retryDelayJitter: true;
|
|
145
|
+
retryDelayJitterMax: number;
|
|
146
|
+
retryIf: null;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
type Config = typeof config;
|
|
150
|
+
|
|
151
|
+
/** Options for automatic retry mechanism */
|
|
152
|
+
type RetryOptions<T = unknown> = {
|
|
153
|
+
/**
|
|
154
|
+
* Maximum number of retries.
|
|
155
|
+
*
|
|
156
|
+
* The total number of attempts will be `retry + 1`.
|
|
157
|
+
*
|
|
158
|
+
* Default: `1`
|
|
159
|
+
*/
|
|
160
|
+
retry?: number;
|
|
161
|
+
/**
|
|
162
|
+
* Accepted values:
|
|
163
|
+
* - exponential: each subsequent retry delay will be doubled from the last
|
|
164
|
+
* - linear: fixed delay between retries
|
|
165
|
+
* Default: 'exponential'
|
|
166
|
+
*/
|
|
167
|
+
retryBackOff?: 'exponential' | 'linear';
|
|
168
|
+
/**
|
|
169
|
+
* Delay in milliseconds between retries.
|
|
170
|
+
* Default: `300`
|
|
171
|
+
*/
|
|
172
|
+
retryDelay?: number;
|
|
173
|
+
/**
|
|
174
|
+
* Add a random delay between 0ms and `retryDelayJitterMax` to the `retryDelayMs`.
|
|
175
|
+
* Default: `true`
|
|
176
|
+
*/
|
|
177
|
+
retryDelayJitter?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Maximum delay (in milliseconds) to be used when randomly generating jitter delay duration.
|
|
180
|
+
* Default: `100`
|
|
181
|
+
*/
|
|
182
|
+
retryDelayJitterMax?: number;
|
|
183
|
+
/**
|
|
184
|
+
* Additional condition/function to be used to determine whether function should be retried.
|
|
185
|
+
* `retryIf` will only be executed when function execution is successful.
|
|
186
|
+
*/
|
|
187
|
+
retryIf?: null | ((prevResult: T | undefined, retryCount: number, error?: unknown) => boolean);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @function PromisE.deferred
|
|
192
|
+
* The adaptation of the `deferred()` function tailored for Promises.
|
|
193
|
+
*
|
|
194
|
+
* @param options (optional) options
|
|
195
|
+
* @property options.delayMs (optional) delay in milliseconds to be used with debounce & throttle modes. When `undefined` or `>= 0`, execution will be sequential.
|
|
196
|
+
* @property options.onError (optional)
|
|
197
|
+
* @property options.onIgnore (optional) invoked whenever callback invocation is ignored by a newer invocation
|
|
198
|
+
* @property options.onResult (optional)
|
|
199
|
+
* @property options.resolveIgnored (optional) see {@link ResolveIgnored}.
|
|
200
|
+
* Default: `PromisE.defaultResolveIgnord` (changeable)
|
|
201
|
+
*
|
|
202
|
+
* @property options.throttle (optional) toggle to switch between debounce/deferred and throttle mode.
|
|
203
|
+
* Requires `defer`.
|
|
204
|
+
* Default: `false`
|
|
205
|
+
*
|
|
206
|
+
* @returns {Function} a callback that is invoked in one of the followin 3 methods:
|
|
207
|
+
* - sequential: when `delayMs <= 0` or `delayMs = undefined`
|
|
208
|
+
* - debounced: when `delayMs > 0` and `throttle = false`
|
|
209
|
+
* - throttled: when `delayMs > 0` and `throttle = true`
|
|
210
|
+
*
|
|
211
|
+
* The main difference is that:
|
|
212
|
+
* - Notes:
|
|
213
|
+
* 1. A "request" simply means invokation of the returned callback function
|
|
214
|
+
* 2. By "handled" it means a "request" will be resolved or rejected.
|
|
215
|
+
* - `PromisE.deferred` is to be used with promises/functions
|
|
216
|
+
* - There is no specific time delay.
|
|
217
|
+
* - The time when a request is completed is irrelevant.
|
|
218
|
+
* - If not throttled:
|
|
219
|
+
* 1. Once a request is handled, all previous requests will be ignored and pool starts anew.
|
|
220
|
+
* 2. If a function is provided in the returned callback, ALL of them will be invoked, regardless of pool size.
|
|
221
|
+
* 3. The last/only request in an on-going requests' pool will handled (resolve/reject).
|
|
222
|
+
* - If throttled:
|
|
223
|
+
* 1. Once a requst starts executing, subsequent requests will be added to a queue.
|
|
224
|
+
* 2. The last/only item in the queue will be handled. Rest will be ignored.
|
|
225
|
+
* 3. If a function is provided in the returned callback, it will be invoked only if the request is handled.
|
|
226
|
+
* Thus, improving performance by avoiding unnecessary invokations.
|
|
227
|
+
* 4. If every single request/function needs to be invoked, avoid using throttle.
|
|
228
|
+
*
|
|
229
|
+
* - If throttled and `strict` is truthy, all subsequent request while a request is being handled will be ignored.
|
|
230
|
+
*
|
|
231
|
+
* @example Explanation & example usage:
|
|
232
|
+
* ```typescript
|
|
233
|
+
* const example = throttle => {
|
|
234
|
+
* const df = PromisE.deferred(throttle)
|
|
235
|
+
* df(() => PromisE.delay(5000)).then(console.log)
|
|
236
|
+
* df(() => PromisE.delay(500)).then(console.log)
|
|
237
|
+
* df(() => PromisE.delay(1000)).then(console.log)
|
|
238
|
+
* // delay 2 seconds and invoke df() again
|
|
239
|
+
* setTimeout(() => {
|
|
240
|
+
* df(() => PromisE.delay(200)).then(console.log)
|
|
241
|
+
* }, 2000)
|
|
242
|
+
* }
|
|
243
|
+
*
|
|
244
|
+
* // Without throttle
|
|
245
|
+
* example(false)
|
|
246
|
+
* // `1000` and `200` will be printed in the console
|
|
247
|
+
*
|
|
248
|
+
* // with throttle
|
|
249
|
+
* example(true)
|
|
250
|
+
* // `5000` and `200` will be printed in the console
|
|
251
|
+
*
|
|
252
|
+
* // with throttle with strict mode
|
|
253
|
+
* example(true)
|
|
254
|
+
* // `5000` will be printed in the console
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
declare function deferred<T>(options?: DeferredOptions): DeferredReturn;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @function PromisE.deferredCallback
|
|
261
|
+
*
|
|
262
|
+
* @summary a `PromisE.deferred()` wrapper for callbacks and event handlers.
|
|
263
|
+
*
|
|
264
|
+
* @returns deferred/throttled function
|
|
265
|
+
*
|
|
266
|
+
*
|
|
267
|
+
* @example Debounce/deferred event handler
|
|
268
|
+
* ```typescript
|
|
269
|
+
* const handleChange = (e: { target: { value: number }}) => console.log(e.target.value)
|
|
270
|
+
* const handleChangeDeferred = PromisE.deferredCallback(handleChange, {
|
|
271
|
+
* delayMs: 300,
|
|
272
|
+
* throttle: false,
|
|
273
|
+
* })
|
|
274
|
+
* // simulate click events call after prespecified delay
|
|
275
|
+
* const delays = [
|
|
276
|
+
* 100,
|
|
277
|
+
* 150,
|
|
278
|
+
* 200,
|
|
279
|
+
* 550,
|
|
280
|
+
* 580,
|
|
281
|
+
* 600,
|
|
282
|
+
* 1000,
|
|
283
|
+
* 1100,
|
|
284
|
+
* ]
|
|
285
|
+
* delays.forEach(timeout =>
|
|
286
|
+
* setTimeout(() => handleChangeDeferred({
|
|
287
|
+
* target: { value: timeout }
|
|
288
|
+
* }), timeout)
|
|
289
|
+
* )
|
|
290
|
+
*
|
|
291
|
+
*
|
|
292
|
+
* // Prints:
|
|
293
|
+
* // 200, 600, 1100
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* @example Throttled event handler
|
|
297
|
+
* ```typescript
|
|
298
|
+
* const handleChange = (e: { target: { value: number }}) => console.log(e.target.value)
|
|
299
|
+
* const handleChangeDeferred = PromisE.deferredCallback(handleChange, {
|
|
300
|
+
* delayMs: 300,
|
|
301
|
+
* throttle: true,
|
|
302
|
+
* })
|
|
303
|
+
* // simulate click events call after prespecified delay
|
|
304
|
+
* const delays = [
|
|
305
|
+
* 100,
|
|
306
|
+
* 150,
|
|
307
|
+
* 200,
|
|
308
|
+
* 550,
|
|
309
|
+
* 580,
|
|
310
|
+
* 600,
|
|
311
|
+
* 1000,
|
|
312
|
+
* 1100,
|
|
313
|
+
* ]
|
|
314
|
+
* delays.forEach(timeout =>
|
|
315
|
+
* setTimeout(() => handleChangeDeferred({
|
|
316
|
+
* target: { value: timeout }
|
|
317
|
+
* }), timeout)
|
|
318
|
+
* )
|
|
319
|
+
* // Prints: 100, 550, 1100
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
declare function deferredCallback<TDefault, CbArgs extends unknown[] = unknown[]>(callback: (...args: CbArgs) => TDefault | Promise<TDefault>, options?: DeferredOptions): <TResult = TDefault>(...args: CbArgs) => IPromisE<TResult>;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @function PromisE.delay
|
|
326
|
+
* @summary Creates a promise that completes after given delay/duration.
|
|
327
|
+
*
|
|
328
|
+
* @param {Number} duration duration in milliseconds
|
|
329
|
+
* @param {unknown} result (optional) specify a value to resolve or reject with.
|
|
330
|
+
* Default: `delayMs` when resolved or timed out error when rejected
|
|
331
|
+
* @param {boolean} asRejected (optional) if `true`, will reject the promise after the delay.
|
|
332
|
+
*
|
|
333
|
+
* @returns See {@link IPromisE_Delay}
|
|
334
|
+
*
|
|
335
|
+
* @example Delay before continuing execution
|
|
336
|
+
* ```typescript
|
|
337
|
+
* import PromisE from '@superutils/promise'
|
|
338
|
+
*
|
|
339
|
+
* console.log('Waiting for app initialization or something else to be ready')
|
|
340
|
+
* // wait 3 seconds before proceeding
|
|
341
|
+
* await PromisE.delay(3000)
|
|
342
|
+
* console.log('App ready')
|
|
343
|
+
* ```
|
|
344
|
+
*
|
|
345
|
+
* @example Execute a function after delay.
|
|
346
|
+
* An awaitable `setTimeout()`.
|
|
347
|
+
* ```typescript
|
|
348
|
+
* import PromisE from '@superutils/promise'
|
|
349
|
+
*
|
|
350
|
+
* PromisE.delay(1000, () => console.log('Prints after 1 second delay'))
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
declare function delay<T = number, TReject extends boolean = boolean>(duration?: number, result?: T | (() => T), asRejected?: TReject): IPromisE_Delay<T>;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @function PromisE.delayReject
|
|
357
|
+
* @summary Creates a promise that rejects after given delay/duration.
|
|
358
|
+
*
|
|
359
|
+
* @example Create a promise that will rejectafter 3 seconds
|
|
360
|
+
* ```typescript
|
|
361
|
+
* const rejectPromise = PromisE.delayReject(
|
|
362
|
+
* 3000, // duration in milliseconds
|
|
363
|
+
* new Error('App did not initialization on time'), // reason to reject with
|
|
364
|
+
* )
|
|
365
|
+
* await rejectPromise // throws error message after 3 seconds
|
|
366
|
+
* codeThatWillNotExecute()
|
|
367
|
+
* ```
|
|
368
|
+
*
|
|
369
|
+
* @example Prevent automated promise rejection by forcing it to resolve before timeout
|
|
370
|
+
* ```typescript
|
|
371
|
+
*
|
|
372
|
+
* const rejectPromise = PromisE.delayReject<string>(
|
|
373
|
+
* 3000,
|
|
374
|
+
* new Error('App did not initialization on time'),
|
|
375
|
+
* )
|
|
376
|
+
* let count = 0
|
|
377
|
+
* const appReady = () => ++count >= 2 // return true on second call
|
|
378
|
+
* const intervalId = setInterval(() => {
|
|
379
|
+
* if (!appReady()) return
|
|
380
|
+
* rejectPromise.resolve('force resolves rejectPromise and execution continues')
|
|
381
|
+
* clearInterval(intervalId)
|
|
382
|
+
* }, 100)
|
|
383
|
+
* await rejectPromise
|
|
384
|
+
* console.log('App is now ready')
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
declare function delayReject<T = never>(duration: number, reason?: unknown): IPromisE_Delay<T>;
|
|
388
|
+
|
|
389
|
+
declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T> {
|
|
390
|
+
readonly state: 0 | 1 | 2;
|
|
391
|
+
private _resolve?;
|
|
392
|
+
private _reject?;
|
|
393
|
+
/**
|
|
394
|
+
* callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve()/reject() methods */
|
|
395
|
+
onEarlyFinalize: OnEarlyFinalize<T>[];
|
|
396
|
+
/** Create a PromisE instance as a drop-in replacement for Promise */
|
|
397
|
+
constructor(...args: PromiseParams<T>);
|
|
398
|
+
/** Extend an existing Promise instance to check status or finalize early */
|
|
399
|
+
constructor(promise: Promise<T>);
|
|
400
|
+
/** Create a resolved promise with value */
|
|
401
|
+
constructor(value: T);
|
|
402
|
+
/**
|
|
403
|
+
* If executor function is not provided, the promise must be resolved/rejected externally.
|
|
404
|
+
*
|
|
405
|
+
* @example An alternative to "Promise.withResolvers()"
|
|
406
|
+
* ```typescript
|
|
407
|
+
* // create a promise that will NEVER finalize automatically
|
|
408
|
+
* const p = new PromisE<number>()
|
|
409
|
+
* // resolve it manually
|
|
410
|
+
* setTimeout(() => p.resolve(1), 1000)
|
|
411
|
+
* p.then(console.log)
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
constructor();
|
|
415
|
+
/** Indicates if the promise is still pending/unfinalized */
|
|
416
|
+
get pending(): boolean;
|
|
417
|
+
/** Indicates if the promise has been rejected */
|
|
418
|
+
get rejected(): boolean;
|
|
419
|
+
/** Indicates if the promise has been resolved */
|
|
420
|
+
get resolved(): boolean;
|
|
421
|
+
/** Resovle pending promise early. */
|
|
422
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
423
|
+
/** Reject pending promise early. */
|
|
424
|
+
reject: (reason: unknown) => void;
|
|
425
|
+
/** Sugar for `new PromisE(Promise.all(...))` */
|
|
426
|
+
static all: <T_1 extends readonly unknown[] | []>(values: T_1) => IPromisE<{ -readonly [P in keyof T_1]: Awaited<T_1[P]>; }>;
|
|
427
|
+
/** Sugar for `new PromisE(Promise.allSettled(...))` */
|
|
428
|
+
static allSettled: <T_1 extends unknown[]>(values: T_1) => IPromisE<PromiseSettledResult<Awaited<T_1[number]>>[]>;
|
|
429
|
+
/** Sugar for `new PromisE(Promise.any(...))` */
|
|
430
|
+
static any: <T_1 extends unknown[]>(values: T_1) => IPromisE<T_1[number]>;
|
|
431
|
+
/** Sugar for `new PromisE(Promise.race(..))` */
|
|
432
|
+
static race: <T_1>(values: T_1[]) => IPromisE<Awaited<T_1>>;
|
|
433
|
+
/** Extends Promise.reject */
|
|
434
|
+
static reject: <T_1 = never>(reason: unknown) => IPromisE<T_1>;
|
|
435
|
+
/** Sugar for `new PromisE(Promise.resolve(...))` */
|
|
436
|
+
static resolve: <T_1>(value?: T_1 | PromiseLike<T_1>) => IPromisE<T_1>;
|
|
437
|
+
/** Sugar for `new PromisE(Promise.try(...))` */
|
|
438
|
+
static try: <T_1, U extends unknown[]>(callbackFn: (...args: U) => T_1 | PromiseLike<T_1>, ...args: U) => IPromisE<Awaited<T_1>>;
|
|
439
|
+
/**
|
|
440
|
+
* Creates a `PromisE` instance and returns it in an object, along with its `resolve` and `reject` functions.
|
|
441
|
+
*
|
|
442
|
+
* NB: this function is technically no longer needed because the `PromisE` class already comes with the resolvers.
|
|
443
|
+
*
|
|
444
|
+
* ---
|
|
445
|
+
* @example
|
|
446
|
+
* Using `PromisE` directly: simply provide an empty function as the executor
|
|
447
|
+
*
|
|
448
|
+
* ```typescript
|
|
449
|
+
* import PromisE from '@superutils/promise'
|
|
450
|
+
* const promisE = new PromisE<number>(() => {})
|
|
451
|
+
* setTimeout(() => promisE.resolve(1), 1000)
|
|
452
|
+
* promisE.then(console.log)
|
|
453
|
+
* ```
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* Using `withResolvers`
|
|
457
|
+
* ```typescript
|
|
458
|
+
* import PromisE from '@superutils/promise'
|
|
459
|
+
* const pwr = PromisE.withResolvers<number>()
|
|
460
|
+
* setTimeout(() => pwr.resolve(1), 1000)
|
|
461
|
+
* pwr.promise.then(console.log)
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
static withResolvers: <T_1 = unknown>() => {
|
|
465
|
+
promise: IPromisE<T_1>;
|
|
466
|
+
reject: (reason: unknown) => void;
|
|
467
|
+
resolve: (value: T_1 | PromiseLike<T_1>) => void;
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* @function PromisE.timeout
|
|
473
|
+
* @summary times out a promise after specified timeout duration.
|
|
474
|
+
*
|
|
475
|
+
* @param timeout (optional) timeout duration in milliseconds.
|
|
476
|
+
* Default: `10000` (10 seconds)
|
|
477
|
+
* @param values promise/function: one or more promises as individual arguments
|
|
478
|
+
*
|
|
479
|
+
* @example Example 1: single promise - resolved
|
|
480
|
+
* ```typescript
|
|
481
|
+
* PromisE.timeout(
|
|
482
|
+
* 5000, // timeout after 5000ms
|
|
483
|
+
* PromisE.delay(1000), // resolves after 1000ms with value 1000
|
|
484
|
+
* ).then(console.log)
|
|
485
|
+
* // Result: 1000
|
|
486
|
+
* ```
|
|
487
|
+
*
|
|
488
|
+
* @example Example 2: multiple promises - resolved
|
|
489
|
+
*
|
|
490
|
+
* ```typescript
|
|
491
|
+
* PromisE.timeout(
|
|
492
|
+
* 5000, // timeout after 5000ms
|
|
493
|
+
* PromisE.delay(1000), // resolves after 1000ms with value 1000
|
|
494
|
+
* PromisE.delay(2000), // resolves after 2000ms with value 2000
|
|
495
|
+
* PromisE.delay(3000), // resolves after 3000ms with value 3000
|
|
496
|
+
* ).then(console.log)
|
|
497
|
+
* // Result: [ 1000, 2000, 3000 ]
|
|
498
|
+
* ```
|
|
499
|
+
*
|
|
500
|
+
* @example Example 3: timed out & rejected
|
|
501
|
+
* ```typescript
|
|
502
|
+
* PromisE.timeout(
|
|
503
|
+
* 5000, // timeout after 5000ms
|
|
504
|
+
* PromisE.delay(20000), // resolves after 20000ms with value 20000
|
|
505
|
+
* ).catch(console.error)
|
|
506
|
+
* // Error: Error('Timed out after 5000ms')
|
|
507
|
+
*```
|
|
508
|
+
*
|
|
509
|
+
* @example Example 4: timed out & but not rejected.
|
|
510
|
+
* // Eg: when API request is taking longer than expected, print a message but not reject the promise.
|
|
511
|
+
* ```typescript
|
|
512
|
+
* const promise = PromisE.timeout(
|
|
513
|
+
* 5000, // timeout after 5000ms
|
|
514
|
+
* PromisE.delay(20000), // data promise, resolves after 20000ms with value 20000
|
|
515
|
+
* )
|
|
516
|
+
* const data = await promise.catch(err => {
|
|
517
|
+
* // promise did not time out, but was rejected because one of the data promises rejected
|
|
518
|
+
* if (!promise.timedout) return Promise.reject(err)
|
|
519
|
+
*
|
|
520
|
+
* // promise timed out >> print/update UI
|
|
521
|
+
* console.log('Request is taking longer than expected......')
|
|
522
|
+
* // now return the data promise (the promise(s) provided in the PromisE.timeout())
|
|
523
|
+
* return promise.data
|
|
524
|
+
* })
|
|
525
|
+
*```
|
|
526
|
+
*/
|
|
527
|
+
declare function timeout<T extends unknown[] | [], TOut = T['length'] extends 1 ? T[0] : T>(timeout?: number, ...values: Promise<TOut>[]): IPromisE_Timeout<TOut>;
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* An attempt to solve the problem of Promise status (pending/resolved/rejected) not being easily accessible externally.
|
|
531
|
+
*
|
|
532
|
+
* For more example see static functions like `PromisE.deferred}, `PromisE.fetch}, `PromisE.timeout} etc.
|
|
533
|
+
*
|
|
534
|
+
*
|
|
535
|
+
* @example Example 1: As a drop-in replacement for Promise class
|
|
536
|
+
* ```typescript
|
|
537
|
+
* import PromisE from '@superutils/promise'
|
|
538
|
+
* const p = new PromisE((resolve, reject) => resolve('done'))
|
|
539
|
+
* console.log(
|
|
540
|
+
* p.pending, // Indicates if promise has finalized (resolved/rejected)
|
|
541
|
+
* p.resolved, // Indicates if the promise has resolved
|
|
542
|
+
* p.rejected // Indicates if the promise has rejected
|
|
543
|
+
* )
|
|
544
|
+
* ```
|
|
545
|
+
*
|
|
546
|
+
* @example Example 2: Extend an existing "Proimse" instance to check status
|
|
547
|
+
* ```typescript
|
|
548
|
+
* import PromisE from '@superutils/promise'
|
|
549
|
+
* const instance = new Promise((resolve) => setTimeout(() => resolve(1), 1000))
|
|
550
|
+
* const p = new PromisE(instance)
|
|
551
|
+
* console.log(p.pending)
|
|
552
|
+
* ```
|
|
553
|
+
*
|
|
554
|
+
* @example Example 3: Create a promise to be finalized externally (an alternative to "PromisE.withResolvers()")
|
|
555
|
+
* ```typescript
|
|
556
|
+
* import PromisE from '@superutils/promise'
|
|
557
|
+
* const p = new PromisE<number>()
|
|
558
|
+
* setTimeout(() => p.resolve(1))
|
|
559
|
+
* p.then(console.log)
|
|
560
|
+
* ```
|
|
561
|
+
*
|
|
562
|
+
* @example Example 4. Invoke functions catching any error and wrapping the result in a PromisE instance
|
|
563
|
+
* ```typescript
|
|
564
|
+
* import PromisE from '@superutils/promise'
|
|
565
|
+
* const p = PromisE.try(() => { throw new Error('I am a naughty function' ) })
|
|
566
|
+
* p.catch(console.error)
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
declare class PromisE<T = unknown> extends PromisEBase<T> {
|
|
570
|
+
/** Global configuration & default values */
|
|
571
|
+
static config: {
|
|
572
|
+
deferOptions: DeferredOptions;
|
|
573
|
+
delayTimeoutMsg: string;
|
|
574
|
+
retryOptions: {
|
|
575
|
+
retry: number;
|
|
576
|
+
retryBackOff: "exponential";
|
|
577
|
+
retryDelay: number;
|
|
578
|
+
retryDelayJitter: true;
|
|
579
|
+
retryDelayJitterMax: number;
|
|
580
|
+
retryIf: null;
|
|
581
|
+
};
|
|
582
|
+
};
|
|
583
|
+
static deferred: typeof deferred;
|
|
584
|
+
static deferredCallback: typeof deferredCallback;
|
|
585
|
+
static delay: typeof delay;
|
|
586
|
+
static delayReject: typeof delayReject;
|
|
587
|
+
static retry: <T_1>(func: () => _superutils_core.ValueOrPromise<T_1>, options?: RetryOptions<T_1>) => Promise<T_1>;
|
|
588
|
+
static timeout: typeof timeout;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Executes a function and retries it on failure or until a specific condition is met.
|
|
593
|
+
*
|
|
594
|
+
* The function will be re-executed if:
|
|
595
|
+
* 1. The `func` promise rejects or the function throws an error.
|
|
596
|
+
* 2. The optional `retryIf` function returns `true`.
|
|
597
|
+
* 3. `retry > 0`
|
|
598
|
+
*
|
|
599
|
+
* Retries will stop when the `retry` count is exhausted, or when `func` executes successfully
|
|
600
|
+
* (resolves without error) AND the `retryIf` (if provided) returns `false`.
|
|
601
|
+
*
|
|
602
|
+
* @template T The type of the value that the `func` returns/resolves to.
|
|
603
|
+
* @param {() => ValueOrPromise<T>} func The function to execute. It can be synchronous or asynchronous.
|
|
604
|
+
* @param {RetryOptions} [options={}] (optional) Options for configuring the retry mechanism.
|
|
605
|
+
* @property {number} [options.retry=1] (optional) The maximum number of retries.
|
|
606
|
+
* @property {number} [options.retryDelayMs=300] The base delay in milliseconds between retries.
|
|
607
|
+
* @property {'exponential' | 'fixed'} [options.retryBackOff='exponential'] The backoff strategy. 'exponential' doubles the delay for each subsequent retry. 'fixed' uses a constant delay.
|
|
608
|
+
* @property {boolean} [options.retryDelayJitter=true] If true, adds a random jitter to the delay to prevent thundering herd problem.
|
|
609
|
+
* @property {number} [options.retryDelayJitterMax=100] The maximum jitter in milliseconds to add to the delay.
|
|
610
|
+
* @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.
|
|
611
|
+
* @returns {Promise<T | undefined>} A promise that resolves with the result of the last successful execution of `func`.
|
|
612
|
+
* If all retries fail (either by throwing an error or by the condition function always returning true),
|
|
613
|
+
* it resolves with `undefined`. Errors thrown by `func` are caught and handled internally, not re-thrown.
|
|
614
|
+
*/
|
|
615
|
+
declare const retry: <T>(func: () => ValueOrPromise<T>, options?: RetryOptions<T>) => Promise<T>;
|
|
616
|
+
|
|
617
|
+
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, config, PromisE as default, deferred, deferredCallback, delay, delayReject, retry, timeout };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
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
|
+
// src/deferred.ts
|
|
38
|
+
import {
|
|
39
|
+
deferred as deferredCore,
|
|
40
|
+
fallbackIfFails as fallbackIfFails2,
|
|
41
|
+
forceCast,
|
|
42
|
+
isFn as isFn2,
|
|
43
|
+
isPositiveNumber,
|
|
44
|
+
throttled as throttledCore
|
|
45
|
+
} from "@superutils/core";
|
|
46
|
+
|
|
47
|
+
// src/PromisEBase.ts
|
|
48
|
+
import { asAny, fallbackIfFails, isFn, isPromise } from "@superutils/core";
|
|
49
|
+
var _PromisEBase = class _PromisEBase extends Promise {
|
|
50
|
+
constructor(input) {
|
|
51
|
+
if (input instanceof _PromisEBase) return input;
|
|
52
|
+
let _resolve;
|
|
53
|
+
let _reject;
|
|
54
|
+
super((resolve, reject) => {
|
|
55
|
+
_reject = (reason) => {
|
|
56
|
+
asAny(this).state = 2;
|
|
57
|
+
reject(reason);
|
|
58
|
+
};
|
|
59
|
+
_resolve = (value) => {
|
|
60
|
+
asAny(this).state = 1;
|
|
61
|
+
resolve(value);
|
|
62
|
+
};
|
|
63
|
+
input != null ? input : input = () => {
|
|
64
|
+
};
|
|
65
|
+
const promise = isPromise(input) ? input : isFn(input) ? new globalThis.Promise(input) : Promise.resolve(input);
|
|
66
|
+
promise.then(_resolve, _reject);
|
|
67
|
+
});
|
|
68
|
+
this.state = 0;
|
|
69
|
+
/**
|
|
70
|
+
* callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve()/reject() methods */
|
|
71
|
+
this.onEarlyFinalize = [];
|
|
72
|
+
//
|
|
73
|
+
//
|
|
74
|
+
// --------------------------- Early resolve/reject ---------------------------
|
|
75
|
+
//
|
|
76
|
+
//
|
|
77
|
+
/** Resovle pending promise early. */
|
|
78
|
+
this.resolve = (value) => {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
if (!this.pending) return;
|
|
81
|
+
(_a = this._resolve) == null ? void 0 : _a.call(this, value);
|
|
82
|
+
(_b = this.onEarlyFinalize) == null ? void 0 : _b.forEach((fn) => {
|
|
83
|
+
fallbackIfFails(fn, [true, value], void 0);
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
/** Reject pending promise early. */
|
|
87
|
+
this.reject = (reason) => {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
if (!this.pending) return;
|
|
90
|
+
(_a = this._reject) == null ? void 0 : _a.call(this, reason);
|
|
91
|
+
(_b = this.onEarlyFinalize) == null ? void 0 : _b.forEach((fn) => {
|
|
92
|
+
fallbackIfFails(fn, [false, reason], void 0);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
this._resolve = _resolve;
|
|
96
|
+
this._reject = _reject;
|
|
97
|
+
}
|
|
98
|
+
//
|
|
99
|
+
//
|
|
100
|
+
//-------------------- Status related read-only attributes --------------------
|
|
101
|
+
//
|
|
102
|
+
//
|
|
103
|
+
/** Indicates if the promise is still pending/unfinalized */
|
|
104
|
+
get pending() {
|
|
105
|
+
return this.state === 0;
|
|
106
|
+
}
|
|
107
|
+
/** Indicates if the promise has been rejected */
|
|
108
|
+
get rejected() {
|
|
109
|
+
return this.state === 2;
|
|
110
|
+
}
|
|
111
|
+
/** Indicates if the promise has been resolved */
|
|
112
|
+
get resolved() {
|
|
113
|
+
return this.state === 1;
|
|
114
|
+
}
|
|
115
|
+
// static withResolvers = <T = unknown>() => {
|
|
116
|
+
// const pwr = globalThis.Promise.withResolvers<T>()
|
|
117
|
+
// const promise = new PromisEBase<T>(pwr.promise) as IPromisE<T>
|
|
118
|
+
// return { ...pwr, promise }
|
|
119
|
+
// }
|
|
120
|
+
};
|
|
121
|
+
//
|
|
122
|
+
//
|
|
123
|
+
// Extend all static `Promise` methods
|
|
124
|
+
//
|
|
125
|
+
//
|
|
126
|
+
/** Sugar for `new PromisE(Promise.all(...))` */
|
|
127
|
+
_PromisEBase.all = (values) => new _PromisEBase(globalThis.Promise.all(values));
|
|
128
|
+
/** Sugar for `new PromisE(Promise.allSettled(...))` */
|
|
129
|
+
_PromisEBase.allSettled = (values) => new _PromisEBase(globalThis.Promise.allSettled(values));
|
|
130
|
+
/** Sugar for `new PromisE(Promise.any(...))` */
|
|
131
|
+
_PromisEBase.any = (values) => new _PromisEBase(globalThis.Promise.any(values));
|
|
132
|
+
/** Sugar for `new PromisE(Promise.race(..))` */
|
|
133
|
+
_PromisEBase.race = (values) => new _PromisEBase(globalThis.Promise.race(values));
|
|
134
|
+
/** Extends Promise.reject */
|
|
135
|
+
_PromisEBase.reject = (reason) => {
|
|
136
|
+
const { promise, reject } = _PromisEBase.withResolvers();
|
|
137
|
+
queueMicrotask(() => reject(reason));
|
|
138
|
+
return promise;
|
|
139
|
+
};
|
|
140
|
+
/** Sugar for `new PromisE(Promise.resolve(...))` */
|
|
141
|
+
_PromisEBase.resolve = (value) => new _PromisEBase(
|
|
142
|
+
globalThis.Promise.resolve(value)
|
|
143
|
+
);
|
|
144
|
+
/** Sugar for `new PromisE(Promise.try(...))` */
|
|
145
|
+
_PromisEBase.try = (callbackFn, ...args) => new _PromisEBase(
|
|
146
|
+
(resolve) => resolve(
|
|
147
|
+
// Promise.try is not supported in Node < 23.
|
|
148
|
+
fallbackIfFails(
|
|
149
|
+
callbackFn,
|
|
150
|
+
args,
|
|
151
|
+
// rethrow error to ensure the returned promise is rejected
|
|
152
|
+
(err) => globalThis.Promise.reject(err)
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
/**
|
|
157
|
+
* Creates a `PromisE` instance and returns it in an object, along with its `resolve` and `reject` functions.
|
|
158
|
+
*
|
|
159
|
+
* NB: this function is technically no longer needed because the `PromisE` class already comes with the resolvers.
|
|
160
|
+
*
|
|
161
|
+
* ---
|
|
162
|
+
* @example
|
|
163
|
+
* Using `PromisE` directly: simply provide an empty function as the executor
|
|
164
|
+
*
|
|
165
|
+
* ```typescript
|
|
166
|
+
* import PromisE from '@superutils/promise'
|
|
167
|
+
* const promisE = new PromisE<number>(() => {})
|
|
168
|
+
* setTimeout(() => promisE.resolve(1), 1000)
|
|
169
|
+
* promisE.then(console.log)
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* Using `withResolvers`
|
|
174
|
+
* ```typescript
|
|
175
|
+
* import PromisE from '@superutils/promise'
|
|
176
|
+
* const pwr = PromisE.withResolvers<number>()
|
|
177
|
+
* setTimeout(() => pwr.resolve(1), 1000)
|
|
178
|
+
* pwr.promise.then(console.log)
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
_PromisEBase.withResolvers = () => {
|
|
182
|
+
const promise = new _PromisEBase();
|
|
183
|
+
return { promise, reject: promise.reject, resolve: promise.resolve };
|
|
184
|
+
};
|
|
185
|
+
var PromisEBase = _PromisEBase;
|
|
186
|
+
var PromisEBase_default = PromisEBase;
|
|
187
|
+
|
|
188
|
+
// src/deferred.ts
|
|
189
|
+
function deferred(options = {}) {
|
|
190
|
+
const defaults = config_default.deferOptions;
|
|
191
|
+
let { onError, onIgnore, onResult } = options;
|
|
192
|
+
const {
|
|
193
|
+
delayMs = defaults.delayMs,
|
|
194
|
+
resolveError = defaults.resolveError,
|
|
195
|
+
// by default reject on error
|
|
196
|
+
resolveIgnored = defaults.resolveIgnored,
|
|
197
|
+
thisArg,
|
|
198
|
+
throttle = defaults.throttle
|
|
199
|
+
} = options;
|
|
200
|
+
let lastPromisE = null;
|
|
201
|
+
const queue = /* @__PURE__ */ new Map();
|
|
202
|
+
const gotDelay = isPositiveNumber(delayMs);
|
|
203
|
+
if (thisArg !== void 0) {
|
|
204
|
+
onError = onError == null ? void 0 : onError.bind(thisArg);
|
|
205
|
+
onIgnore = onIgnore == null ? void 0 : onIgnore.bind(thisArg);
|
|
206
|
+
onResult = onResult == null ? void 0 : onResult.bind(thisArg);
|
|
207
|
+
}
|
|
208
|
+
const ignoreOrProceed = (currentId, qItem) => {
|
|
209
|
+
lastPromisE = null;
|
|
210
|
+
if (!gotDelay) {
|
|
211
|
+
queue.delete(currentId);
|
|
212
|
+
const [nextId, nextItem] = [...queue.entries()][0] || [];
|
|
213
|
+
return nextId && nextItem && execute(nextId, nextItem);
|
|
214
|
+
}
|
|
215
|
+
const items = [...queue.entries()];
|
|
216
|
+
const currentIndex = items.findIndex(([id]) => id === currentId);
|
|
217
|
+
for (let i = 0; i <= currentIndex; i++) {
|
|
218
|
+
const [iId, iItem] = items[i];
|
|
219
|
+
queue.delete(iId);
|
|
220
|
+
if (iItem == void 0 || iItem.started) continue;
|
|
221
|
+
onIgnore && fallbackIfFails2(onIgnore, [iItem.getPromise], void 0);
|
|
222
|
+
switch (resolveIgnored) {
|
|
223
|
+
case "WITH_UNDEFINED" /* WITH_UNDEFINED */:
|
|
224
|
+
iItem.resolve(void 0);
|
|
225
|
+
break;
|
|
226
|
+
case "WITH_LAST" /* WITH_LAST */:
|
|
227
|
+
qItem == null ? void 0 : qItem.then(iItem.resolve, iItem.reject);
|
|
228
|
+
break;
|
|
229
|
+
case "NEVER" /* NEVER */:
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const finalizeCb = (resolve, id, qItem) => (resultOrErr) => {
|
|
235
|
+
ignoreOrProceed(id, qItem);
|
|
236
|
+
if (resolve) {
|
|
237
|
+
qItem.resolve(resultOrErr);
|
|
238
|
+
onResult && fallbackIfFails2(onResult, [resultOrErr], void 0);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
onError && fallbackIfFails2(onError, [resultOrErr], void 0);
|
|
242
|
+
switch (resolveError) {
|
|
243
|
+
case "REJECT" /* REJECT */:
|
|
244
|
+
qItem.reject(resultOrErr);
|
|
245
|
+
// eslint-disable-next-line no-fallthrough
|
|
246
|
+
case "NEVER" /* NEVER */:
|
|
247
|
+
break;
|
|
248
|
+
case "RESOLVE_UNDEFINED" /* WITH_UNDEFINED */:
|
|
249
|
+
resultOrErr = void 0;
|
|
250
|
+
// eslint-disable-next-line no-fallthrough
|
|
251
|
+
case "RESOLVE_ERROR" /* WITH_ERROR */:
|
|
252
|
+
qItem.resolve(resultOrErr);
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const execute = (() => {
|
|
257
|
+
const execute2 = (id, qItem) => {
|
|
258
|
+
qItem.started = true;
|
|
259
|
+
lastPromisE = new PromisEBase_default(qItem.getPromise());
|
|
260
|
+
lastPromisE.then(
|
|
261
|
+
finalizeCb(true, id, qItem),
|
|
262
|
+
finalizeCb(false, id, qItem)
|
|
263
|
+
);
|
|
264
|
+
};
|
|
265
|
+
if (!gotDelay) return execute2;
|
|
266
|
+
const deferFn = throttle ? throttledCore : deferredCore;
|
|
267
|
+
return deferFn(execute2, delayMs, options);
|
|
268
|
+
})();
|
|
269
|
+
const deferredFunc = (promise) => {
|
|
270
|
+
const id = Symbol("deferred-queue-item-id");
|
|
271
|
+
const qItem = new PromisEBase_default();
|
|
272
|
+
qItem.getPromise = isFn2(promise) ? promise : () => promise;
|
|
273
|
+
qItem.started = false;
|
|
274
|
+
queue.set(id, qItem);
|
|
275
|
+
if (gotDelay || !lastPromisE) execute(id, qItem);
|
|
276
|
+
return forceCast(qItem);
|
|
277
|
+
};
|
|
278
|
+
return deferredFunc;
|
|
279
|
+
}
|
|
280
|
+
var deferred_default = deferred;
|
|
281
|
+
|
|
282
|
+
// src/deferredCallback.ts
|
|
283
|
+
function deferredCallback(callback, options = {}) {
|
|
284
|
+
const { thisArg } = options;
|
|
285
|
+
if (thisArg !== void 0) callback = callback.bind(thisArg);
|
|
286
|
+
const deferPromise = deferred_default(options);
|
|
287
|
+
return (...args) => deferPromise(() => callback(...args));
|
|
288
|
+
}
|
|
289
|
+
var deferredCallback_default = deferredCallback;
|
|
290
|
+
|
|
291
|
+
// src/delay.ts
|
|
292
|
+
import { fallbackIfFails as fallbackIfFails3, isFn as isFn3 } from "@superutils/core";
|
|
293
|
+
function delay(duration = 100, result = duration, asRejected = false) {
|
|
294
|
+
const promise = new PromisEBase_default();
|
|
295
|
+
const finalize = (result2) => {
|
|
296
|
+
var _a;
|
|
297
|
+
if (isFn3(result2))
|
|
298
|
+
result2 = (_a = fallbackIfFails3(result2, [], void 0)) != null ? _a : duration;
|
|
299
|
+
if (!asRejected) return promise.resolve(result2);
|
|
300
|
+
promise.reject(
|
|
301
|
+
result2 != null ? result2 : new Error(`${config_default.delayTimeoutMsg} ${duration}ms`)
|
|
302
|
+
);
|
|
303
|
+
};
|
|
304
|
+
promise.timeoutId = setTimeout(() => finalize(result), duration);
|
|
305
|
+
promise.pause = () => clearTimeout(promise.timeoutId);
|
|
306
|
+
promise.catch(() => {
|
|
307
|
+
}).finally(() => promise.pause());
|
|
308
|
+
return promise;
|
|
309
|
+
}
|
|
310
|
+
var delay_default = delay;
|
|
311
|
+
|
|
312
|
+
// src/delayReject.ts
|
|
313
|
+
function delayReject(duration, reason) {
|
|
314
|
+
return delay_default(duration, reason, true);
|
|
315
|
+
}
|
|
316
|
+
var delayReject_default = delayReject;
|
|
317
|
+
|
|
318
|
+
// src/retry.ts
|
|
319
|
+
import { isPositiveInteger } from "@superutils/core";
|
|
320
|
+
var retry = async (func, options = {}) => {
|
|
321
|
+
const d = config_default.retryOptions;
|
|
322
|
+
const {
|
|
323
|
+
retryIf,
|
|
324
|
+
retryBackOff = d.retryBackOff,
|
|
325
|
+
retryDelayJitter = d.retryDelayJitter
|
|
326
|
+
} = options;
|
|
327
|
+
let {
|
|
328
|
+
retry: maxRetries = 1,
|
|
329
|
+
retryDelay: delayMs,
|
|
330
|
+
retryDelayJitterMax: jitterMax
|
|
331
|
+
} = options;
|
|
332
|
+
maxRetries = maxRetries >= 0 ? maxRetries : d.retry;
|
|
333
|
+
delayMs = isPositiveInteger(delayMs) ? delayMs : d.retryDelay;
|
|
334
|
+
jitterMax = isPositiveInteger(jitterMax) ? jitterMax : d.retryDelayJitterMax;
|
|
335
|
+
let retryCount = -1;
|
|
336
|
+
let result;
|
|
337
|
+
let error;
|
|
338
|
+
let shouldRetry = false;
|
|
339
|
+
do {
|
|
340
|
+
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);
|
|
344
|
+
try {
|
|
345
|
+
error = void 0;
|
|
346
|
+
result = await func();
|
|
347
|
+
} catch (err) {
|
|
348
|
+
error = err;
|
|
349
|
+
}
|
|
350
|
+
shouldRetry = maxRetries > 0 && (!!error || !!(retryIf == null ? void 0 : retryIf(result, retryCount, error))) && retryCount < maxRetries;
|
|
351
|
+
} while (shouldRetry);
|
|
352
|
+
if (error !== void 0) return Promise.reject(error);
|
|
353
|
+
return result;
|
|
354
|
+
};
|
|
355
|
+
var retry_default = retry;
|
|
356
|
+
|
|
357
|
+
// src/timeout.ts
|
|
358
|
+
function timeout(timeout2 = 1e4, ...values) {
|
|
359
|
+
const dataPromise = values.length === 1 ? new PromisEBase_default(values[0]) : PromisEBase_default.all(values);
|
|
360
|
+
const timeoutPromise = delayReject_default(
|
|
361
|
+
timeout2,
|
|
362
|
+
new Error(`Timed out after ${timeout2}ms`)
|
|
363
|
+
);
|
|
364
|
+
const promise = PromisEBase_default.race([
|
|
365
|
+
dataPromise,
|
|
366
|
+
timeoutPromise
|
|
367
|
+
]);
|
|
368
|
+
promise.clearTimeout = () => clearTimeout(timeoutPromise.timeoutId);
|
|
369
|
+
promise.data = dataPromise;
|
|
370
|
+
promise.timeout = timeoutPromise;
|
|
371
|
+
Object.defineProperty(promise, "timedout", {
|
|
372
|
+
get: () => promise.timeout.rejected
|
|
373
|
+
});
|
|
374
|
+
dataPromise.catch(() => {
|
|
375
|
+
}).finally(promise.clearTimeout);
|
|
376
|
+
return promise;
|
|
377
|
+
}
|
|
378
|
+
var timeout_default = timeout;
|
|
379
|
+
|
|
380
|
+
// src/PromisE.ts
|
|
381
|
+
var PromisE = class extends PromisEBase_default {
|
|
382
|
+
};
|
|
383
|
+
/** Global configuration & default values */
|
|
384
|
+
PromisE.config = config_default;
|
|
385
|
+
PromisE.deferred = deferred_default;
|
|
386
|
+
PromisE.deferredCallback = deferredCallback_default;
|
|
387
|
+
PromisE.delay = delay_default;
|
|
388
|
+
PromisE.delayReject = delayReject_default;
|
|
389
|
+
PromisE.retry = retry_default;
|
|
390
|
+
PromisE.timeout = timeout_default;
|
|
391
|
+
var PromisE_default = PromisE;
|
|
392
|
+
|
|
393
|
+
// src/index.ts
|
|
394
|
+
var index_default = PromisE_default;
|
|
395
|
+
export {
|
|
396
|
+
PromisE,
|
|
397
|
+
PromisEBase,
|
|
398
|
+
ResolveError,
|
|
399
|
+
ResolveIgnored,
|
|
400
|
+
config,
|
|
401
|
+
index_default as default,
|
|
402
|
+
deferred,
|
|
403
|
+
deferredCallback,
|
|
404
|
+
delay,
|
|
405
|
+
delayReject,
|
|
406
|
+
retry,
|
|
407
|
+
timeout
|
|
408
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Toufiqur Rahaman Chowdhury",
|
|
3
|
+
"bugs": {
|
|
4
|
+
"url": "https://github.com/alien45/superutils/issues"
|
|
5
|
+
},
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@superutils/core": "^1.0.1"
|
|
8
|
+
},
|
|
9
|
+
"description": "An extended Promise class with extra features and utilities.",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/alien45/superutils/#readme",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"promise",
|
|
18
|
+
"async",
|
|
19
|
+
"util",
|
|
20
|
+
"typescript"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"main": "dist/index.js",
|
|
24
|
+
"name": "@superutils/promise",
|
|
25
|
+
"peerDpendencies": {
|
|
26
|
+
"@superutils/core": "^1.0.1"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/alien45/superutils.git"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"_build": "tsc -p tsconfig.json",
|
|
37
|
+
"_watch": "tsc -p tsconfig.json --watch",
|
|
38
|
+
"build": "tsup src/index.ts --format esm --dts --clean --config ../../tsup.config.js",
|
|
39
|
+
"dev": "npm run build -- --watch",
|
|
40
|
+
"test": "vitest"
|
|
41
|
+
},
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"type": "module",
|
|
44
|
+
"types": "dist/index.d.ts",
|
|
45
|
+
"version": "1.0.1"
|
|
46
|
+
}
|