@superutils/promise 1.0.9 → 1.1.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/README.md +46 -27
- package/dist/index.d.ts +27 -14
- package/dist/index.js +79 -54
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -117,7 +117,7 @@ while (!appReady) {
|
|
|
117
117
|
}
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
#### `PromisE.delay(duration, callback)`: execute after delay
|
|
120
|
+
#### `PromisE.delay(duration, callback, asRejected)`: execute after delay
|
|
121
121
|
|
|
122
122
|
Creates a promise that executes a function after a specified duration and returns the value the function returns.
|
|
123
123
|
|
|
@@ -126,10 +126,13 @@ If callback returns undefined, default value will be the duration.
|
|
|
126
126
|
```typescript
|
|
127
127
|
import PromisE from '@superutils/promise'
|
|
128
128
|
|
|
129
|
-
const
|
|
130
|
-
|
|
129
|
+
const func = async () => {
|
|
130
|
+
console.log('Waiting for app initialization or something else to be ready')
|
|
131
|
+
// wait 3 seconds before proceeding
|
|
132
|
+
await PromisE.delay(3000)
|
|
133
|
+
console.log('App ready')
|
|
131
134
|
}
|
|
132
|
-
|
|
135
|
+
func()
|
|
133
136
|
```
|
|
134
137
|
|
|
135
138
|
<div id="deferred"></div>
|
|
@@ -138,32 +141,48 @@ await PromisE.delay(100, callback)
|
|
|
138
141
|
|
|
139
142
|
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.
|
|
140
143
|
|
|
141
|
-
|
|
142
|
-
import PromisE, { ResolveIgnored } from '@superutils/promise'
|
|
144
|
+
#### Debounce example:
|
|
143
145
|
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
```typescript
|
|
147
|
+
const example = async (options = {}) => {
|
|
148
|
+
const df = PromisE.deferred({
|
|
149
|
+
delayMs: 100,
|
|
150
|
+
resolveIgnored: ResolveIgnored.NEVER, // never resolve ignored calls
|
|
151
|
+
...options,
|
|
152
|
+
})
|
|
153
|
+
df(() => PromisE.delay(500)).then(console.log)
|
|
154
|
+
df(() => PromisE.delay(1000)).then(console.log)
|
|
155
|
+
df(() => PromisE.delay(5000)).then(console.log)
|
|
156
|
+
// delay 2 seconds and invoke df() again
|
|
157
|
+
await PromisE.delay(2000)
|
|
158
|
+
df(() => PromisE.delay(200)).then(console.log)
|
|
159
|
+
}
|
|
160
|
+
example({ ignoreStale: false, throttle: false })
|
|
161
|
+
// `200` and `1000` will be printed in the console
|
|
162
|
+
example({ ignoreStale: true, throttle: false })
|
|
163
|
+
// `200` will be printed in the console
|
|
164
|
+
```
|
|
149
165
|
|
|
150
|
-
|
|
151
|
-
* USE WITH CAUTION!
|
|
152
|
-
*/
|
|
153
|
-
resolveIgnored: ResolveIgnored.NEVER,
|
|
166
|
+
#### Throttle example:
|
|
154
167
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
)
|
|
168
|
+
```typescript
|
|
169
|
+
const example = async (options = {}) => {
|
|
170
|
+
const df = PromisE.deferred({
|
|
171
|
+
delayMs: 100,
|
|
172
|
+
resolveIgnored: ResolveIgnored.NEVER, // never resolve ignored calls
|
|
173
|
+
...options,
|
|
174
|
+
})
|
|
175
|
+
df(() => PromisE.delay(5000)).then(console.log)
|
|
176
|
+
df(() => PromisE.delay(500)).then(console.log)
|
|
177
|
+
df(() => PromisE.delay(1000)).then(console.log)
|
|
178
|
+
// delay 2 seconds and invoke df() again
|
|
179
|
+
await PromisE.delay(2000)
|
|
180
|
+
df(() => PromisE.delay(200)).then(console.log)
|
|
181
|
+
}
|
|
182
|
+
example({ ignoreStale: true, throttle: true })
|
|
183
|
+
// `200` will be printed in the console
|
|
184
|
+
example({ ignoreStale: false, throttle: true })
|
|
185
|
+
// `200` and `5000` will be printed in the console
|
|
167
186
|
```
|
|
168
187
|
|
|
169
188
|
<div id="deferredCallback"></div>
|
package/dist/index.d.ts
CHANGED
|
@@ -85,14 +85,18 @@ type DeferredAsyncDefaults = Pick<Required<DeferredAsyncOptions>, 'delayMs' | 'r
|
|
|
85
85
|
/** Options for `PromisE.deferred` and other related functions */
|
|
86
86
|
type DeferredAsyncOptions<ThisArg = unknown, DelayMs extends number = number> = {
|
|
87
87
|
/**
|
|
88
|
-
* Delay in milliseconds, used for `debounce` and `throttle` modes.
|
|
88
|
+
* Delay in milliseconds, used for `debounce` and `throttle` modes. Use `0` for sequential execution.
|
|
89
89
|
*
|
|
90
|
-
*
|
|
90
|
+
* Use `0` to disable debounce/throttle and execute all operations sequentially.
|
|
91
|
+
*
|
|
92
|
+
* Default: `100` (or what is set in `PromisE.deferred.defaults.delayMs`)
|
|
91
93
|
*/
|
|
94
|
+
delayMs?: 0 | PositiveNumber<DelayMs>;
|
|
95
|
+
ignoreStale?: boolean;
|
|
92
96
|
/** Callback invoked whenever promise/function throws error */
|
|
93
97
|
onError?: (this: ThisArg, err: unknown) => ValueOrPromise<unknown>;
|
|
94
98
|
/**
|
|
95
|
-
* Whenever a promise/function is ignored when in
|
|
99
|
+
* Whenever a promise/function is ignored when in debounce/throttle mode, `onIgnored` wil be invoked.
|
|
96
100
|
* The promise/function will not be invoked, unless it's manually invoked using the `ignored` function.
|
|
97
101
|
* Use for debugging or logging purposes.
|
|
98
102
|
*/
|
|
@@ -118,17 +122,13 @@ type DeferredAsyncOptions<ThisArg = unknown, DelayMs extends number = number> =
|
|
|
118
122
|
thisArg?: ThisArg;
|
|
119
123
|
} & (({
|
|
120
124
|
/** Throttle duration in milliseconds */
|
|
121
|
-
delayMs
|
|
125
|
+
delayMs: PositiveNumber<DelayMs>;
|
|
122
126
|
throttle: true;
|
|
123
|
-
} &
|
|
127
|
+
} & Pick<ThrottleOptions, 'trailing'>) | ({
|
|
124
128
|
/** Debounce/deferred duration in milliseconds */
|
|
125
129
|
delayMs?: PositiveNumber<DelayMs>;
|
|
126
130
|
throttle?: false;
|
|
127
|
-
} &
|
|
128
|
-
/** Sequential execution. All promises will be executed sequentially making sure there is no overlap. */
|
|
129
|
-
delayMs: 0;
|
|
130
|
-
throttle?: false;
|
|
131
|
-
});
|
|
131
|
+
} & Pick<DeferredOptions, 'leading'>));
|
|
132
132
|
/** Determines what to do when deferred promise/function fails */
|
|
133
133
|
declare enum ResolveError {
|
|
134
134
|
/** Neither resolve nor reject the failed */
|
|
@@ -176,7 +176,7 @@ type RetryOptions<T = unknown> = {
|
|
|
176
176
|
*/
|
|
177
177
|
retryDelay?: number;
|
|
178
178
|
/**
|
|
179
|
-
* Add a random delay between 0ms and `retryDelayJitterMax` to the `
|
|
179
|
+
* Add a random delay between 0ms and `retryDelayJitterMax` to the `retryDelay`.
|
|
180
180
|
* Default: `true`
|
|
181
181
|
*/
|
|
182
182
|
retryDelayJitter?: boolean;
|
|
@@ -224,7 +224,13 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
|
|
|
224
224
|
get rejected(): boolean;
|
|
225
225
|
/** Indicates if the promise has been resolved */
|
|
226
226
|
get resolved(): boolean;
|
|
227
|
-
/**
|
|
227
|
+
/**
|
|
228
|
+
* Get promise status code:
|
|
229
|
+
*
|
|
230
|
+
* - `0` = pending
|
|
231
|
+
* - `1` = resolved
|
|
232
|
+
* - `2` = rejected
|
|
233
|
+
*/
|
|
228
234
|
get state(): 0 | 1 | 2;
|
|
229
235
|
/** Resovle pending promise early. */
|
|
230
236
|
resolve: (value: T | PromiseLike<T>) => void;
|
|
@@ -352,7 +358,7 @@ type TimeoutOptions<Func extends string = 'all'> = {
|
|
|
352
358
|
* @example Explanation & example usage:
|
|
353
359
|
* ```typescript
|
|
354
360
|
* const example = throttle => {
|
|
355
|
-
* const df = PromisE.deferred(throttle)
|
|
361
|
+
* const df = PromisE.deferred({ delayMs: 100, throttle })
|
|
356
362
|
* df(() => PromisE.delay(5000)).then(console.log)
|
|
357
363
|
* df(() => PromisE.delay(500)).then(console.log)
|
|
358
364
|
* df(() => PromisE.delay(1000)).then(console.log)
|
|
@@ -378,8 +384,15 @@ type TimeoutOptions<Func extends string = 'all'> = {
|
|
|
378
384
|
declare function deferred<T, ThisArg = unknown, Delay extends number = number>(options?: DeferredAsyncOptions<ThisArg, Delay>): DeferredAsyncCallback;
|
|
379
385
|
declare namespace deferred {
|
|
380
386
|
var defaults: {
|
|
387
|
+
/**
|
|
388
|
+
* Default delay in milliseconds, used in `debounce` and `throttle` modes.
|
|
389
|
+
*
|
|
390
|
+
* Use `0` (or negative number) to disable debounce/throttle and execute all operations sequentially.
|
|
391
|
+
*/
|
|
381
392
|
delayMs: number;
|
|
393
|
+
/** Set the default error resolution behavior. {@link ResolveError} for all options. */
|
|
382
394
|
resolveError: ResolveError.REJECT;
|
|
395
|
+
/** Set the default ignored resolution behavior. See {@link ResolveIgnored} for all options. */
|
|
383
396
|
resolveIgnored: ResolveIgnored.WITH_LAST;
|
|
384
397
|
};
|
|
385
398
|
}
|
|
@@ -686,7 +699,7 @@ declare class PromisE<T = unknown> extends PromisEBase<T> {
|
|
|
686
699
|
* - `'linear'` uses a constant delay.
|
|
687
700
|
*
|
|
688
701
|
* Default: `'exponential'`
|
|
689
|
-
* @param options.
|
|
702
|
+
* @param options.retryDelay (optional) The base delay in milliseconds between retries.
|
|
690
703
|
*
|
|
691
704
|
* Default: `300`
|
|
692
705
|
* @param options.retryDelayJitter (optional) If true, adds a random jitter to the delay to prevent
|
package/dist/index.js
CHANGED
|
@@ -76,7 +76,13 @@ var _PromisEBase = class _PromisEBase extends Promise {
|
|
|
76
76
|
get resolved() {
|
|
77
77
|
return this._state === 1;
|
|
78
78
|
}
|
|
79
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Get promise status code:
|
|
81
|
+
*
|
|
82
|
+
* - `0` = pending
|
|
83
|
+
* - `1` = resolved
|
|
84
|
+
* - `2` = rejected
|
|
85
|
+
*/
|
|
80
86
|
get state() {
|
|
81
87
|
return this._state;
|
|
82
88
|
}
|
|
@@ -170,105 +176,124 @@ var ResolveIgnored = /* @__PURE__ */ ((ResolveIgnored2) => {
|
|
|
170
176
|
|
|
171
177
|
// src/deferred.ts
|
|
172
178
|
function deferred(options = {}) {
|
|
173
|
-
|
|
179
|
+
let sequence = 0;
|
|
174
180
|
options = objCopy(
|
|
175
|
-
defaults,
|
|
181
|
+
deferred.defaults,
|
|
176
182
|
options,
|
|
177
183
|
[],
|
|
178
184
|
"empty"
|
|
179
185
|
);
|
|
180
186
|
let { onError, onIgnore, onResult } = options;
|
|
181
187
|
const {
|
|
182
|
-
delayMs,
|
|
188
|
+
delayMs = 0,
|
|
183
189
|
resolveError,
|
|
184
|
-
// by default reject on error
|
|
185
190
|
resolveIgnored,
|
|
186
191
|
thisArg,
|
|
187
192
|
throttle
|
|
188
193
|
} = options;
|
|
189
|
-
let
|
|
194
|
+
let prevQItem = null;
|
|
190
195
|
const queue = /* @__PURE__ */ new Map();
|
|
191
|
-
const
|
|
196
|
+
const isSequential = !isPositiveNumber(delayMs);
|
|
192
197
|
if (thisArg !== void 0) {
|
|
193
198
|
onError = onError == null ? void 0 : onError.bind(thisArg);
|
|
194
199
|
onIgnore = onIgnore == null ? void 0 : onIgnore.bind(thisArg);
|
|
195
200
|
onResult = onResult == null ? void 0 : onResult.bind(thisArg);
|
|
196
201
|
}
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
if (!gotDelay) {
|
|
200
|
-
queue.delete(currentId);
|
|
201
|
-
const [nextId, nextItem] = [...queue.entries()][0] || [];
|
|
202
|
-
return nextId && nextItem && execute(nextId, nextItem);
|
|
203
|
-
}
|
|
204
|
-
const items = [...queue.entries()];
|
|
205
|
-
const currentIndex = items.findIndex(([id]) => id === currentId);
|
|
206
|
-
for (let i = 0; i <= currentIndex; i++) {
|
|
207
|
-
const [iId, iItem] = items[i];
|
|
202
|
+
const handleIgnore = (items, prevQItem2) => {
|
|
203
|
+
for (const [iId, iItem] of items) {
|
|
208
204
|
queue.delete(iId);
|
|
209
|
-
if (iItem
|
|
210
|
-
onIgnore && fallbackIfFails2(onIgnore, [iItem.getPromise],
|
|
205
|
+
if (iItem === void 0 || iItem.started) continue;
|
|
206
|
+
onIgnore && fallbackIfFails2(onIgnore, [iItem.getPromise], 0);
|
|
211
207
|
switch (resolveIgnored) {
|
|
212
208
|
case "WITH_UNDEFINED" /* WITH_UNDEFINED */:
|
|
213
209
|
iItem.resolve(void 0);
|
|
214
210
|
break;
|
|
215
211
|
case "WITH_LAST" /* WITH_LAST */:
|
|
216
|
-
|
|
212
|
+
prevQItem2 == null ? void 0 : prevQItem2.then(iItem.resolve, iItem.reject);
|
|
217
213
|
break;
|
|
218
214
|
case "NEVER" /* NEVER */:
|
|
219
215
|
break;
|
|
220
216
|
}
|
|
221
217
|
}
|
|
218
|
+
if (!queue.size) sequence = 0;
|
|
222
219
|
};
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
220
|
+
const handleRemaining = (currentId) => {
|
|
221
|
+
const _prevQItem = prevQItem;
|
|
222
|
+
prevQItem = null;
|
|
223
|
+
if (isSequential) {
|
|
224
|
+
queue.delete(currentId);
|
|
225
|
+
const [nextId, nextItem] = [...queue.entries()][0] || [];
|
|
226
|
+
return nextId && nextItem && handleItem(nextId, nextItem);
|
|
229
227
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
resultOrErr = void 0;
|
|
239
|
-
// eslint-disable-next-line no-fallthrough
|
|
240
|
-
case "RESOLVE_ERROR" /* WITH_ERROR */:
|
|
241
|
-
qItem.resolve(resultOrErr);
|
|
242
|
-
break;
|
|
228
|
+
let items = [...queue.entries()];
|
|
229
|
+
if (throttle && options.trailing) {
|
|
230
|
+
items = items.slice(
|
|
231
|
+
0,
|
|
232
|
+
items.findIndex(([id]) => id === currentId)
|
|
233
|
+
);
|
|
234
|
+
} else if (!throttle && options.leading) {
|
|
235
|
+
items = items.slice(0, -1);
|
|
243
236
|
}
|
|
237
|
+
handleIgnore(items, _prevQItem);
|
|
244
238
|
};
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
239
|
+
let prevSeq = 0;
|
|
240
|
+
const executeItem = async (id, qItem) => {
|
|
241
|
+
var _a;
|
|
242
|
+
qItem.started = true;
|
|
243
|
+
const _prevQItem = prevQItem;
|
|
244
|
+
prevQItem = qItem;
|
|
245
|
+
prevSeq = (_a = prevQItem == null ? void 0 : prevQItem.sequence) != null ? _a : 0;
|
|
246
|
+
try {
|
|
247
|
+
const result = await PromisEBase_default.try(qItem.getPromise);
|
|
248
|
+
const ignore = !isSequential && options.ignoreStale && prevSeq > qItem.sequence;
|
|
249
|
+
if (ignore) return handleIgnore([[id, qItem]], _prevQItem);
|
|
250
|
+
qItem.resolve(result);
|
|
251
|
+
onResult && fallbackIfFails2(onResult, [result], void 0);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
onError && fallbackIfFails2(onError, [err], void 0);
|
|
254
|
+
switch (resolveError) {
|
|
255
|
+
case "REJECT" /* REJECT */:
|
|
256
|
+
qItem.reject(err);
|
|
257
|
+
// eslint-disable-next-line no-fallthrough
|
|
258
|
+
case "NEVER" /* NEVER */:
|
|
259
|
+
break;
|
|
260
|
+
case "RESOLVE_UNDEFINED" /* WITH_UNDEFINED */:
|
|
261
|
+
qItem.resolve(void 0);
|
|
262
|
+
break;
|
|
263
|
+
case "RESOLVE_ERROR" /* WITH_ERROR */:
|
|
264
|
+
qItem.resolve(err);
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
handleRemaining(id);
|
|
269
|
+
};
|
|
270
|
+
const handleItem = isSequential ? executeItem : (throttle ? throttledCore : deferredCore)(
|
|
271
|
+
executeItem,
|
|
272
|
+
delayMs,
|
|
273
|
+
options
|
|
274
|
+
);
|
|
258
275
|
const deferredFunc = (promise) => {
|
|
259
276
|
const id = /* @__PURE__ */ Symbol("deferred-queue-item-id");
|
|
260
277
|
const qItem = new PromisEBase_default();
|
|
261
278
|
qItem.getPromise = isFn2(promise) ? promise : () => promise;
|
|
262
279
|
qItem.started = false;
|
|
280
|
+
qItem.sequence = ++sequence;
|
|
263
281
|
queue.set(id, qItem);
|
|
264
|
-
if (
|
|
282
|
+
if (!prevQItem || !isSequential) handleItem(id, qItem);
|
|
265
283
|
return qItem;
|
|
266
284
|
};
|
|
267
285
|
return deferredFunc;
|
|
268
286
|
}
|
|
269
287
|
deferred.defaults = {
|
|
288
|
+
/**
|
|
289
|
+
* Default delay in milliseconds, used in `debounce` and `throttle` modes.
|
|
290
|
+
*
|
|
291
|
+
* Use `0` (or negative number) to disable debounce/throttle and execute all operations sequentially.
|
|
292
|
+
*/
|
|
270
293
|
delayMs: 100,
|
|
294
|
+
/** Set the default error resolution behavior. {@link ResolveError} for all options. */
|
|
271
295
|
resolveError: "REJECT" /* REJECT */,
|
|
296
|
+
/** Set the default ignored resolution behavior. See {@link ResolveIgnored} for all options. */
|
|
272
297
|
resolveIgnored: "WITH_LAST" /* WITH_LAST */
|
|
273
298
|
};
|
|
274
299
|
var deferred_default = deferred;
|
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.
|
|
7
|
+
"@superutils/core": "^1.1.1"
|
|
8
8
|
},
|
|
9
9
|
"description": "An extended Promise with extra features such as status tracking, deferred/throttled execution, timeout and retry mechanism.",
|
|
10
10
|
"files": [
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"main": "dist/index.js",
|
|
24
24
|
"name": "@superutils/promise",
|
|
25
25
|
"peerDpendencies": {
|
|
26
|
-
"@superutils/core": "^1.
|
|
26
|
+
"@superutils/core": "^1.1.1"
|
|
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.
|
|
46
|
+
"version": "1.1.1"
|
|
47
47
|
}
|