@superutils/promise 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -85,14 +85,17 @@ 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
- * Default: `100`
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>;
92
95
  /** Callback invoked whenever promise/function throws error */
93
96
  onError?: (this: ThisArg, err: unknown) => ValueOrPromise<unknown>;
94
97
  /**
95
- * Whenever a promise/function is ignored when in debource/throttle mode, `onIgnored` wil be invoked.
98
+ * Whenever a promise/function is ignored when in debounce/throttle mode, `onIgnored` wil be invoked.
96
99
  * The promise/function will not be invoked, unless it's manually invoked using the `ignored` function.
97
100
  * Use for debugging or logging purposes.
98
101
  */
@@ -118,17 +121,13 @@ type DeferredAsyncOptions<ThisArg = unknown, DelayMs extends number = number> =
118
121
  thisArg?: ThisArg;
119
122
  } & (({
120
123
  /** Throttle duration in milliseconds */
121
- delayMs?: PositiveNumber<DelayMs>;
124
+ delayMs: PositiveNumber<DelayMs>;
122
125
  throttle: true;
123
- } & Omit<ThrottleOptions, 'onError' | 'ThisArg' | 'tid'>) | ({
126
+ } & Pick<ThrottleOptions, 'trailing'>) | ({
124
127
  /** Debounce/deferred duration in milliseconds */
125
128
  delayMs?: PositiveNumber<DelayMs>;
126
129
  throttle?: false;
127
- } & Omit<DeferredOptions, 'onError' | 'ThisArg' | 'tid'>) | {
128
- /** Sequential execution. All promises will be executed sequentially making sure there is no overlap. */
129
- delayMs: 0;
130
- throttle?: false;
131
- });
130
+ } & Pick<DeferredOptions, 'leading'>));
132
131
  /** Determines what to do when deferred promise/function fails */
133
132
  declare enum ResolveError {
134
133
  /** Neither resolve nor reject the failed */
@@ -176,7 +175,7 @@ type RetryOptions<T = unknown> = {
176
175
  */
177
176
  retryDelay?: number;
178
177
  /**
179
- * Add a random delay between 0ms and `retryDelayJitterMax` to the `retryDelayMs`.
178
+ * Add a random delay between 0ms and `retryDelayJitterMax` to the `retryDelay`.
180
179
  * Default: `true`
181
180
  */
182
181
  retryDelayJitter?: boolean;
@@ -224,7 +223,13 @@ declare class PromisEBase<T = unknown> extends Promise<T> implements IPromisE<T>
224
223
  get rejected(): boolean;
225
224
  /** Indicates if the promise has been resolved */
226
225
  get resolved(): boolean;
227
- /** Get promise status code */
226
+ /**
227
+ * Get promise status code:
228
+ *
229
+ * - `0` = pending
230
+ * - `1` = resolved
231
+ * - `2` = rejected
232
+ */
228
233
  get state(): 0 | 1 | 2;
229
234
  /** Resovle pending promise early. */
230
235
  resolve: (value: T | PromiseLike<T>) => void;
@@ -352,7 +357,7 @@ type TimeoutOptions<Func extends string = 'all'> = {
352
357
  * @example Explanation & example usage:
353
358
  * ```typescript
354
359
  * const example = throttle => {
355
- * const df = PromisE.deferred(throttle)
360
+ * const df = PromisE.deferred({ delayMs: 100, throttle })
356
361
  * df(() => PromisE.delay(5000)).then(console.log)
357
362
  * df(() => PromisE.delay(500)).then(console.log)
358
363
  * df(() => PromisE.delay(1000)).then(console.log)
@@ -378,8 +383,15 @@ type TimeoutOptions<Func extends string = 'all'> = {
378
383
  declare function deferred<T, ThisArg = unknown, Delay extends number = number>(options?: DeferredAsyncOptions<ThisArg, Delay>): DeferredAsyncCallback;
379
384
  declare namespace deferred {
380
385
  var defaults: {
386
+ /**
387
+ * Default delay in milliseconds, used in `debounce` and `throttle` modes.
388
+ *
389
+ * Use `0` (or negative number) to disable debounce/throttle and execute all operations sequentially.
390
+ */
381
391
  delayMs: number;
392
+ /** Set the default error resolution behavior. {@link ResolveError} for all options. */
382
393
  resolveError: ResolveError.REJECT;
394
+ /** Set the default ignored resolution behavior. See {@link ResolveIgnored} for all options. */
383
395
  resolveIgnored: ResolveIgnored.WITH_LAST;
384
396
  };
385
397
  }
@@ -686,7 +698,7 @@ declare class PromisE<T = unknown> extends PromisEBase<T> {
686
698
  * - `'linear'` uses a constant delay.
687
699
  *
688
700
  * Default: `'exponential'`
689
- * @param options.retryDelayMs (optional) The base delay in milliseconds between retries.
701
+ * @param options.retryDelay (optional) The base delay in milliseconds between retries.
690
702
  *
691
703
  * Default: `300`
692
704
  * @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
- /** Get promise status code */
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,116 @@ var ResolveIgnored = /* @__PURE__ */ ((ResolveIgnored2) => {
170
176
 
171
177
  // src/deferred.ts
172
178
  function deferred(options = {}) {
173
- const { defaults } = deferred;
174
179
  options = objCopy(
175
- defaults,
180
+ deferred.defaults,
176
181
  options,
177
182
  [],
178
183
  "empty"
179
184
  );
180
185
  let { onError, onIgnore, onResult } = options;
181
186
  const {
182
- delayMs,
187
+ delayMs = 0,
183
188
  resolveError,
184
- // by default reject on error
185
189
  resolveIgnored,
186
190
  thisArg,
187
191
  throttle
188
192
  } = options;
189
- let lastPromisE = null;
193
+ let prevQItem = null;
190
194
  const queue = /* @__PURE__ */ new Map();
191
- const gotDelay = isPositiveNumber(delayMs);
195
+ const isSequential = !isPositiveNumber(delayMs);
192
196
  if (thisArg !== void 0) {
193
197
  onError = onError == null ? void 0 : onError.bind(thisArg);
194
198
  onIgnore = onIgnore == null ? void 0 : onIgnore.bind(thisArg);
195
199
  onResult = onResult == null ? void 0 : onResult.bind(thisArg);
196
200
  }
197
- const ignoreOrProceed = (currentId, qItem) => {
198
- lastPromisE = null;
199
- if (!gotDelay) {
201
+ const handleRemaining = (currentId) => {
202
+ const _prevQItem = prevQItem;
203
+ prevQItem = null;
204
+ if (isSequential) {
200
205
  queue.delete(currentId);
201
206
  const [nextId, nextItem] = [...queue.entries()][0] || [];
202
- return nextId && nextItem && execute(nextId, nextItem);
207
+ return nextId && nextItem && handleItem(nextId, nextItem);
208
+ }
209
+ let items = [...queue.entries()];
210
+ if (throttle && options.trailing) {
211
+ items = items.slice(
212
+ 0,
213
+ items.findIndex(([id]) => id === currentId)
214
+ );
215
+ } else if (!throttle && options.leading) {
216
+ items = items.slice(0, -1);
203
217
  }
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];
208
- queue.delete(iId);
209
- if (iItem == void 0 || iItem.started) continue;
210
- onIgnore && fallbackIfFails2(onIgnore, [iItem.getPromise], void 0);
218
+ for (const [iId, iItem] of items) {
219
+ !iItem.completed && queue.delete(iId);
220
+ if (iItem === void 0 || iItem.started || iItem.completed)
221
+ continue;
222
+ iItem.completed = true;
223
+ onIgnore && fallbackIfFails2(onIgnore, [iItem.getPromise], 0);
211
224
  switch (resolveIgnored) {
212
225
  case "WITH_UNDEFINED" /* WITH_UNDEFINED */:
213
226
  iItem.resolve(void 0);
214
227
  break;
215
228
  case "WITH_LAST" /* WITH_LAST */:
216
- qItem == null ? void 0 : qItem.then(iItem.resolve, iItem.reject);
229
+ _prevQItem == null ? void 0 : _prevQItem.then(iItem.resolve, iItem.reject);
217
230
  break;
218
231
  case "NEVER" /* NEVER */:
219
232
  break;
220
233
  }
221
234
  }
222
235
  };
223
- const finalizeCb = (resolve, id, qItem) => (resultOrErr) => {
224
- ignoreOrProceed(id, qItem);
225
- if (resolve) {
226
- qItem.resolve(resultOrErr);
227
- onResult && fallbackIfFails2(onResult, [resultOrErr], void 0);
228
- return;
229
- }
230
- onError && fallbackIfFails2(onError, [resultOrErr], void 0);
231
- switch (resolveError) {
232
- case "REJECT" /* REJECT */:
233
- qItem.reject(resultOrErr);
234
- // eslint-disable-next-line no-fallthrough
235
- case "NEVER" /* NEVER */:
236
- break;
237
- case "RESOLVE_UNDEFINED" /* WITH_UNDEFINED */:
238
- resultOrErr = void 0;
239
- // eslint-disable-next-line no-fallthrough
240
- case "RESOLVE_ERROR" /* WITH_ERROR */:
241
- qItem.resolve(resultOrErr);
242
- break;
236
+ const executeItem = async (id, qItem) => {
237
+ if (!id || !(qItem == null ? void 0 : qItem.pending) || qItem.completed) return;
238
+ qItem.started = true;
239
+ prevQItem = qItem;
240
+ try {
241
+ const result = await PromisEBase_default.try(qItem.getPromise);
242
+ qItem.resolve(result);
243
+ onResult && fallbackIfFails2(onResult, [result], void 0);
244
+ } catch (err) {
245
+ onError && fallbackIfFails2(onError, [err], void 0);
246
+ switch (resolveError) {
247
+ case "REJECT" /* REJECT */:
248
+ qItem.reject(err);
249
+ // eslint-disable-next-line no-fallthrough
250
+ case "NEVER" /* NEVER */:
251
+ break;
252
+ case "RESOLVE_UNDEFINED" /* WITH_UNDEFINED */:
253
+ qItem.resolve(void 0);
254
+ break;
255
+ case "RESOLVE_ERROR" /* WITH_ERROR */:
256
+ qItem.resolve(err);
257
+ break;
258
+ }
243
259
  }
260
+ qItem.completed = true;
261
+ handleRemaining(id);
244
262
  };
245
- const execute = (() => {
246
- const execute2 = (id, qItem) => {
247
- qItem.started = true;
248
- lastPromisE = new PromisEBase_default(qItem.getPromise());
249
- lastPromisE.then(
250
- finalizeCb(true, id, qItem),
251
- finalizeCb(false, id, qItem)
252
- );
253
- };
254
- if (!gotDelay) return execute2;
255
- const deferFn = throttle ? throttledCore : deferredCore;
256
- return deferFn(execute2, delayMs, options);
257
- })();
263
+ const handleItem = isSequential ? executeItem : (throttle ? throttledCore : deferredCore)(
264
+ executeItem,
265
+ delayMs,
266
+ options
267
+ );
258
268
  const deferredFunc = (promise) => {
259
269
  const id = /* @__PURE__ */ Symbol("deferred-queue-item-id");
260
270
  const qItem = new PromisEBase_default();
261
271
  qItem.getPromise = isFn2(promise) ? promise : () => promise;
262
272
  qItem.started = false;
263
273
  queue.set(id, qItem);
264
- if (gotDelay || !lastPromisE) execute(id, qItem);
274
+ if (!prevQItem || !isSequential) handleItem(id, qItem);
265
275
  return qItem;
266
276
  };
267
277
  return deferredFunc;
268
278
  }
269
279
  deferred.defaults = {
280
+ /**
281
+ * Default delay in milliseconds, used in `debounce` and `throttle` modes.
282
+ *
283
+ * Use `0` (or negative number) to disable debounce/throttle and execute all operations sequentially.
284
+ */
270
285
  delayMs: 100,
286
+ /** Set the default error resolution behavior. {@link ResolveError} for all options. */
271
287
  resolveError: "REJECT" /* REJECT */,
288
+ /** Set the default ignored resolution behavior. See {@link ResolveIgnored} for all options. */
272
289
  resolveIgnored: "WITH_LAST" /* WITH_LAST */
273
290
  };
274
291
  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.0.8"
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.0.8"
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.0.9"
46
+ "version": "1.1.0"
47
47
  }