@superutils/promise 1.0.1 → 1.0.3

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 CHANGED
@@ -1,9 +1,15 @@
1
- # @superutils/promise
1
+ # [@superutils/promise](https://www.npmjs.com/package/@superutils/promise)
2
2
 
3
3
  An extended `Promise` implementation, named `PromisE`, that provides additional features and utilities for easier asynchronous flow control in JavaScript and TypeScript applications.
4
4
 
5
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
6
 
7
+ <div v-if="false">
8
+
9
+ For full API reference check out the [docs page](https://alien45.github.io/superutils/packages/@superutils/promise/).
10
+
11
+ </div>
12
+
7
13
  ## Table of Contents
8
14
 
9
15
  - [Features](#features)
package/dist/index.d.ts CHANGED
@@ -101,6 +101,10 @@ type DeferredOptions<ThisArg = unknown> = {
101
101
  * See {@link ResolveIgnored} for available options.
102
102
  */
103
103
  resolveIgnored?: ResolveIgnored;
104
+ /**
105
+ * What do to when an executed function/promise throws error
106
+ * See {@link ResolveError} for available options.
107
+ */
104
108
  resolveError?: ResolveError;
105
109
  /** Enable throttle mode. Requires {@link DeferredOptions.delayMs}*/
106
110
  throttle?: boolean;
@@ -187,6 +191,108 @@ type RetryOptions<T = unknown> = {
187
191
  retryIf?: null | ((prevResult: T | undefined, retryCount: number, error?: unknown) => boolean);
188
192
  };
189
193
 
194
+ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T> {
195
+ readonly state: 0 | 1 | 2;
196
+ private _resolve?;
197
+ private _reject?;
198
+ /**
199
+ * callbacks to be invoked whenever PromisE instance is finalized early using non-static resolve()/reject() methods */
200
+ onEarlyFinalize: OnEarlyFinalize<T>[];
201
+ /** Create a PromisE instance as a drop-in replacement for Promise */
202
+ constructor(...args: PromiseParams<T>);
203
+ /** Extend an existing Promise instance to check status or finalize early */
204
+ constructor(promise: Promise<T>);
205
+ /** Create a resolved promise with value */
206
+ constructor(value: T);
207
+ /**
208
+ * If executor function is not provided, the promise must be resolved/rejected externally.
209
+ *
210
+ * @example An alternative to "Promise.withResolvers()"
211
+ * ```typescript
212
+ * // create a promise that will NEVER finalize automatically
213
+ * const p = new PromisE<number>()
214
+ * // resolve it manually
215
+ * setTimeout(() => p.resolve(1), 1000)
216
+ * p.then(console.log)
217
+ * ```
218
+ */
219
+ constructor();
220
+ /** Indicates if the promise is still pending/unfinalized */
221
+ get pending(): boolean;
222
+ /** Indicates if the promise has been rejected */
223
+ get rejected(): boolean;
224
+ /** Indicates if the promise has been resolved */
225
+ get resolved(): boolean;
226
+ /** Resovle pending promise early. */
227
+ resolve: (value: T | PromiseLike<T>) => void;
228
+ /** Reject pending promise early. */
229
+ reject: (reason: unknown) => void;
230
+ /** Sugar for `new PromisE(Promise.all(...))` */
231
+ static all: <T_1 extends unknown[]>(values: T_1) => IPromisE<{ -readonly [P in keyof T_1]: Awaited<T_1[P]>; }>;
232
+ /** Sugar for `new PromisE(Promise.allSettled(...))` */
233
+ static allSettled: <T_1 extends unknown[]>(values: T_1) => IPromisE<PromiseSettledResult<Awaited<T_1[number]>>[]>;
234
+ /** Sugar for `new PromisE(Promise.any(...))` */
235
+ static any: <T_1 extends unknown[]>(values: T_1) => IPromisE<Awaited<T_1[number]>>;
236
+ /** Sugar for `new PromisE(Promise.race(..))` */
237
+ static race: <T_1 extends unknown[]>(values: T_1) => IPromisE<Awaited<T_1[number]>>;
238
+ /** Extends Promise.reject */
239
+ static reject: <T_1 = never>(reason: unknown) => IPromisE<T_1>;
240
+ /** Sugar for `new PromisE(Promise.resolve(...))` */
241
+ static resolve: <T_1>(value?: T_1 | PromiseLike<T_1>) => IPromisE<T_1>;
242
+ /** Sugar for `new PromisE(Promise.try(...))` */
243
+ static try: <T_1, U extends unknown[]>(callbackFn: (...args: U) => T_1 | PromiseLike<T_1>, ...args: U) => IPromisE<Awaited<T_1>>;
244
+ /**
245
+ * Creates a `PromisE` instance and returns it in an object, along with its `resolve` and `reject` functions.
246
+ *
247
+ * NB: this function is technically no longer needed because the `PromisE` class already comes with the resolvers.
248
+ *
249
+ * ---
250
+ * @example
251
+ * Using `PromisE` directly: simply provide an empty function as the executor
252
+ *
253
+ * ```typescript
254
+ * import PromisE from '@superutils/promise'
255
+ * const promisE = new PromisE<number>(() => {})
256
+ * setTimeout(() => promisE.resolve(1), 1000)
257
+ * promisE.then(console.log)
258
+ * ```
259
+ *
260
+ * @example
261
+ * Using `withResolvers`
262
+ * ```typescript
263
+ * import PromisE from '@superutils/promise'
264
+ * const pwr = PromisE.withResolvers<number>()
265
+ * setTimeout(() => pwr.resolve(1), 1000)
266
+ * pwr.promise.then(console.log)
267
+ * ```
268
+ */
269
+ static withResolvers: <T_1 = unknown>() => {
270
+ promise: IPromisE<T_1>;
271
+ reject: (reason: unknown) => void;
272
+ resolve: (value: T_1 | PromiseLike<T_1>) => void;
273
+ };
274
+ }
275
+
276
+ type TimeoutFunc<T extends unknown[]> = {
277
+ all: typeof PromisEBase.all<T>;
278
+ allSettled: typeof PromisEBase.allSettled<T>;
279
+ any: typeof PromisEBase.any<T>;
280
+ race: typeof PromisEBase.race<T>;
281
+ };
282
+ /**
283
+ * `PromisE.timeout` options
284
+ *
285
+ * @param func (optional) name of the supported `PromieBase` static method. Default: `"all"`
286
+ * @param timeout (optional) timeout duration in milliseconds. Default: `10_000` (10 seconds)
287
+ * @param timeoutMsg (optional) timeout error message. Default: `"Timed out after 10000ms"`
288
+ *
289
+ */
290
+ type TimeoutOptions<Func extends string = 'all'> = {
291
+ func: Func;
292
+ timeout?: number;
293
+ timeoutMsg?: string;
294
+ };
295
+
190
296
  /**
191
297
  * @function PromisE.deferred
192
298
  * The adaptation of the `deferred()` function tailored for Promises.
@@ -266,58 +372,27 @@ declare function deferred<T>(options?: DeferredOptions): DeferredReturn;
266
372
  *
267
373
  * @example Debounce/deferred event handler
268
374
  * ```typescript
269
- * const handleChange = (e: { target: { value: number }}) => console.log(e.target.value)
375
+ * import PromisE from '@superutils/promise'
376
+ *
377
+ * // Input change handler
378
+ * const handleChange = (e: { target: { value: number } }) =>
379
+ * console.log(e.target.value)
380
+ * // Change handler with `PromisE.deferred()`
270
381
  * const handleChangeDeferred = PromisE.deferredCallback(handleChange, {
271
- * delayMs: 300,
272
- * throttle: false,
382
+ * delayMs: 300,
383
+ * throttle: false,
273
384
  * })
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
- * ]
385
+ * // Simulate input change events after prespecified delays
386
+ * const delays = [100, 150, 200, 550, 580, 600, 1000, 1100]
285
387
  * delays.forEach(timeout =>
286
- * setTimeout(() => handleChangeDeferred({
287
- * target: { value: timeout }
288
- * }), timeout)
388
+ * setTimeout(
389
+ * () => handleChangeDeferred({ target: { value: timeout } }),
390
+ * timeout,
391
+ * ),
289
392
  * )
290
- *
291
- *
292
393
  * // Prints:
293
394
  * // 200, 600, 1100
294
395
  * ```
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
396
  */
322
397
  declare function deferredCallback<TDefault, CbArgs extends unknown[] = unknown[]>(callback: (...args: CbArgs) => TDefault | Promise<TDefault>, options?: DeferredOptions): <TResult = TDefault>(...args: CbArgs) => IPromisE<TResult>;
323
398
 
@@ -386,88 +461,6 @@ declare function delay<T = number, TReject extends boolean = boolean>(duration?:
386
461
  */
387
462
  declare function delayReject<T = never>(duration: number, reason?: unknown): IPromisE_Delay<T>;
388
463
 
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
464
  /**
472
465
  * @function PromisE.timeout
473
466
  * @summary times out a promise after specified timeout duration.
@@ -524,7 +517,11 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
524
517
  * })
525
518
  *```
526
519
  */
527
- declare function timeout<T extends unknown[] | [], TOut = T['length'] extends 1 ? T[0] : T>(timeout?: number, ...values: Promise<TOut>[]): IPromisE_Timeout<TOut>;
520
+ declare function timeout<T extends [unknown, ...unknown[]], // require at least one value
521
+ TFunc extends keyof TimeoutFunc<T>, Result = T['length'] extends 1 ? Awaited<T[0]> : Awaited<ReturnType<TimeoutFunc<T>[TFunc]>>>(timeout: number | TimeoutOptions<TFunc>, ...values: T): IPromisE_Timeout<Result>;
522
+ declare namespace timeout {
523
+ var defaultOptions: Required<TimeoutOptions>;
524
+ }
528
525
 
529
526
  /**
530
527
  * An attempt to solve the problem of Promise status (pending/resolved/rejected) not being easily accessible externally.
@@ -614,4 +611,4 @@ declare class PromisE<T = unknown> extends PromisEBase<T> {
614
611
  */
615
612
  declare const retry: <T>(func: () => ValueOrPromise<T>, options?: RetryOptions<T>) => Promise<T>;
616
613
 
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 };
614
+ export { type Config, type DeferredOptions, type DeferredReturn, type IPromisE, type IPromisE_Delay, type IPromisE_Timeout, type OnEarlyFinalize, PromisE, PromisEBase, type PromiseParams, ResolveError, ResolveIgnored, type RetryOptions, type TimeoutFunc, type TimeoutOptions, config, PromisE as default, deferred, deferredCallback, delay, delayReject, retry, timeout };
package/dist/index.js CHANGED
@@ -325,13 +325,13 @@ var retry = async (func, options = {}) => {
325
325
  retryDelayJitter = d.retryDelayJitter
326
326
  } = options;
327
327
  let {
328
- retry: maxRetries = 1,
328
+ retry: maxRetries = d.retry,
329
329
  retryDelay: delayMs,
330
330
  retryDelayJitterMax: jitterMax
331
331
  } = options;
332
- maxRetries = maxRetries >= 0 ? maxRetries : d.retry;
333
- delayMs = isPositiveInteger(delayMs) ? delayMs : d.retryDelay;
334
- jitterMax = isPositiveInteger(jitterMax) ? jitterMax : d.retryDelayJitterMax;
332
+ if (maxRetries !== 0 && !isPositiveInteger(maxRetries)) maxRetries = d.retry;
333
+ if (!isPositiveInteger(delayMs)) delayMs = d.retryDelay;
334
+ if (!isPositiveInteger(jitterMax)) jitterMax = d.retryDelayJitterMax;
335
335
  let retryCount = -1;
336
336
  let result;
337
337
  let error;
@@ -347,7 +347,7 @@ var retry = async (func, options = {}) => {
347
347
  } catch (err) {
348
348
  error = err;
349
349
  }
350
- shouldRetry = maxRetries > 0 && (!!error || !!(retryIf == null ? void 0 : retryIf(result, retryCount, error))) && retryCount < maxRetries;
350
+ shouldRetry = maxRetries > 0 && retryCount < maxRetries && (!!error || !!(retryIf == null ? void 0 : retryIf(result, retryCount, error)));
351
351
  } while (shouldRetry);
352
352
  if (error !== void 0) return Promise.reject(error);
353
353
  return result;
@@ -355,11 +355,22 @@ var retry = async (func, options = {}) => {
355
355
  var retry_default = retry;
356
356
 
357
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);
358
+ import { isFn as isFn4, isObj, isPositiveNumber as isPositiveNumber2 } from "@superutils/core";
359
+ function timeout(timeout2, ...values) {
360
+ var _a, _b;
361
+ let funcName = "all";
362
+ let timeoutMsg = "";
363
+ if (isObj(timeout2)) {
364
+ funcName = timeout2.func;
365
+ timeoutMsg = (_a = timeout2.timeoutMsg) != null ? _a : "";
366
+ timeout2 = (_b = timeout2.timeout) != null ? _b : 1e4;
367
+ }
368
+ timeout2 = isPositiveNumber2(timeout2) && timeout2 || 1e4;
369
+ const func = isFn4(PromisEBase_default[funcName]) ? PromisEBase_default[funcName] : PromisEBase_default.all;
370
+ const dataPromise = values.length <= 1 ? new PromisEBase_default(values == null ? void 0 : values[0]) : func(values);
360
371
  const timeoutPromise = delayReject_default(
361
372
  timeout2,
362
- new Error(`Timed out after ${timeout2}ms`)
373
+ new Error(timeoutMsg || `Timed out after ${timeout2}ms`)
363
374
  );
364
375
  const promise = PromisEBase_default.race([
365
376
  dataPromise,
@@ -372,9 +383,15 @@ function timeout(timeout2 = 1e4, ...values) {
372
383
  get: () => promise.timeout.rejected
373
384
  });
374
385
  dataPromise.catch(() => {
375
- }).finally(promise.clearTimeout);
386
+ }).finally(() => {
387
+ promise.clearTimeout();
388
+ });
376
389
  return promise;
377
390
  }
391
+ timeout.defaultOptions = {
392
+ func: "all",
393
+ timeout: 1e4
394
+ };
378
395
  var timeout_default = timeout;
379
396
 
380
397
  // src/PromisE.ts
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.0.1"
7
+ "@superutils/core": "1.0.3"
8
8
  },
9
9
  "description": "An extended Promise class with extra features and utilities.",
10
10
  "files": [
@@ -12,7 +12,7 @@
12
12
  "README.md",
13
13
  "LICENSE"
14
14
  ],
15
- "homepage": "https://github.com/alien45/superutils/#readme",
15
+ "homepage": "https://alien45.github.io/superutils/packages/@superutils/promise/",
16
16
  "keywords": [
17
17
  "promise",
18
18
  "async",
@@ -23,7 +23,7 @@
23
23
  "main": "dist/index.js",
24
24
  "name": "@superutils/promise",
25
25
  "peerDpendencies": {
26
- "@superutils/core": "^1.0.1"
26
+ "@superutils/core": "1.0.3"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
@@ -37,10 +37,10 @@
37
37
  "_watch": "tsc -p tsconfig.json --watch",
38
38
  "build": "tsup src/index.ts --format esm --dts --clean --config ../../tsup.config.js",
39
39
  "dev": "npm run build -- --watch",
40
- "test": "vitest"
40
+ "test": "cd ../../ && npm run test promise"
41
41
  },
42
42
  "sideEffects": false,
43
43
  "type": "module",
44
44
  "types": "dist/index.d.ts",
45
- "version": "1.0.1"
45
+ "version": "1.0.3"
46
46
  }