@dr.pogodin/js-utils 0.0.13 → 0.0.15

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.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # MIT License
2
2
 
3
- _Copyright © 2023, Dr. Sergey Pogodin_
4
- &mdash; <doc@pogodin.studio> (https://dr.pogodin.studio) \
3
+ _Copyright &copy; 2023&ndash;2025, Dr. Sergey Pogodin_
4
+ &mdash; <doc@pogodin.studio> (https://dr.pogodin.studio)
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
package/babel.config.js CHANGED
@@ -1,4 +1,6 @@
1
- module.exports = {
1
+ // Babel is used for Jest testing.
2
+
3
+ export default {
2
4
  presets: [
3
5
  ['@babel/preset-env', { targets: { node: 'current' } }],
4
6
  '@babel/preset-typescript',
package/build/Barrier.js CHANGED
@@ -1,5 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
1
  var STATE;
4
2
  (function (STATE) {
5
3
  STATE["PENDING"] = "PENDING";
@@ -29,7 +27,7 @@ var STATE;
29
27
  *
30
28
  * Docs: https://dr.pogodin.studio/docs/react-utils/docs/api/classes/Barrier
31
29
  */
32
- class Barrier extends Promise {
30
+ export default class Barrier extends Promise {
33
31
  constructor(executor) {
34
32
  let resolveRef;
35
33
  let rejectRef;
@@ -88,4 +86,3 @@ class Barrier extends Promise {
88
86
  return res;
89
87
  }
90
88
  }
91
- exports.default = Barrier;
package/build/Emitter.js CHANGED
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Emitter = void 0;
4
1
  /**
5
2
  * Simple listeneable data Emitter.
6
3
  */
7
- class Emitter {
4
+ export class Emitter {
8
5
  constructor() {
9
6
  this.p_listeners = [];
10
7
  }
@@ -53,4 +50,3 @@ class Emitter {
53
50
  this.p_listeners.splice(idx, 1);
54
51
  }
55
52
  }
56
- exports.Emitter = Emitter;
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
2
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
3
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,15 +7,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
8
  });
10
9
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- const Barrier_1 = __importDefault(require("./Barrier"));
10
+ import Barrier from './Barrier';
16
11
  /**
17
12
  * Implements a simple semaphore for async code logic.
18
13
  */
19
- class Semaphore {
14
+ export default class Semaphore {
20
15
  constructor(ready = false) {
21
16
  // "true" when the drain queue process is running (and thus no need to start
22
17
  // a new one).
@@ -55,7 +50,7 @@ class Semaphore {
55
50
  waitReady() {
56
51
  return __awaiter(this, arguments, void 0, function* (seize = false) {
57
52
  if (!this.p_ready || this.p_queue.length) {
58
- const barrier = new Barrier_1.default();
53
+ const barrier = new Barrier();
59
54
  this.p_queue.push(barrier);
60
55
  yield barrier;
61
56
  if (seize)
@@ -77,7 +72,7 @@ class Semaphore {
77
72
  return __awaiter(this, void 0, void 0, function* () {
78
73
  this.p_draining = true;
79
74
  while (this.p_ready && this.p_queue.length) {
80
- this.p_drainLock = new Barrier_1.default();
75
+ this.p_drainLock = new Barrier();
81
76
  this.p_queue[0].resolve();
82
77
  yield this.p_drainLock; // eslint-disable-line no-await-in-loop
83
78
  this.p_queue.shift();
@@ -87,4 +82,3 @@ class Semaphore {
87
82
  });
88
83
  }
89
84
  }
90
- exports.default = Semaphore;
package/build/index.js CHANGED
@@ -1,28 +1,5 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- var __importDefault = (this && this.__importDefault) || function (mod) {
17
- return (mod && mod.__esModule) ? mod : { "default": mod };
18
- };
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.withRetries = exports.Semaphore = exports.Barrier = void 0;
21
- var Barrier_1 = require("./Barrier");
22
- Object.defineProperty(exports, "Barrier", { enumerable: true, get: function () { return __importDefault(Barrier_1).default; } });
23
- __exportStar(require("./Emitter"), exports);
24
- var Semaphore_1 = require("./Semaphore");
25
- Object.defineProperty(exports, "Semaphore", { enumerable: true, get: function () { return __importDefault(Semaphore_1).default; } });
26
- __exportStar(require("./time"), exports);
27
- var withRetries_1 = require("./withRetries");
28
- Object.defineProperty(exports, "withRetries", { enumerable: true, get: function () { return __importDefault(withRetries_1).default; } });
1
+ export { default as Barrier } from './Barrier';
2
+ export * from './Emitter';
3
+ export { default as Semaphore } from './Semaphore';
4
+ export * from './time';
5
+ export { default as withRetries } from './withRetries';
package/build/time.js CHANGED
@@ -1,24 +1,17 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Timer = exports.YEAR_MS = exports.DAY_MS = exports.HOUR_MS = exports.MIN_MS = exports.SEC_MS = void 0;
7
- exports.timer = timer;
8
- const Barrier_1 = __importDefault(require("./Barrier"));
1
+ import Barrier from './Barrier';
9
2
  // This is not very elegant, but as of now TypeScript does not support type
10
3
  // arithmetic, thus we can't have constants assigned like `MIN_MS = 60 * SEC_MS`
11
4
  // and have the result type to be 60000 (number literal), it would be just
12
5
  // the generic number type.
13
- exports.SEC_MS = 1000;
14
- exports.MIN_MS = 60000; // 60 * SEC_MS
15
- exports.HOUR_MS = 3600000; // 60 * MIN_MS
16
- exports.DAY_MS = 86400000; // 24 * HOUR_MS
17
- exports.YEAR_MS = 31536000000; // 365 * DAY_MS
6
+ export const SEC_MS = 1000;
7
+ export const MIN_MS = 60000; // 60 * SEC_MS
8
+ export const HOUR_MS = 3600000; // 60 * MIN_MS
9
+ export const DAY_MS = 86400000; // 24 * HOUR_MS
10
+ export const YEAR_MS = 31536000000; // 365 * DAY_MS
18
11
  // TODO: Ok, as we have ended up with a Timer class, mostly to achieve a good
19
12
  // TypeScript typing for timer() function, it makes sense to expose the class
20
13
  // from the library as well, and it should be documented later.
21
- class Timer extends Barrier_1.default {
14
+ export class Timer extends Barrier {
22
15
  get abort() { return this.p_abort; }
23
16
  get timeout() { return this.p_timeout; }
24
17
  /**
@@ -59,7 +52,6 @@ class Timer extends Barrier_1.default {
59
52
  return res;
60
53
  }
61
54
  }
62
- exports.Timer = Timer;
63
55
  /**
64
56
  * Creates a Promise, which resolves after the given timeout.
65
57
  * @param {number} timeout Timeout [ms].
@@ -67,7 +59,7 @@ exports.Timer = Timer;
67
59
  * .abort() method attached, which cancels the pending timer resolution
68
60
  * (without resolving or rejecting the barrier).
69
61
  */
70
- function timer(timeout) {
62
+ export function timer(timeout) {
71
63
  const t = new Timer();
72
64
  return t.init(timeout);
73
65
  }
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
2
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
3
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,9 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
8
  });
10
9
  };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.default = withRetries;
13
- const time_1 = require("./time");
10
+ import { timer } from './time';
14
11
  /**
15
12
  * Attempts to perform the given async `action` up to `maxRetries` times with
16
13
  * the specified `interval`, stopping at the first successful (non-throwing)
@@ -22,7 +19,7 @@ const time_1 = require("./time");
22
19
  * @returns Resolves to the result of the successful `action` execution;
23
20
  * or rejects with the error from the last faileda attempt.
24
21
  */
25
- function withRetries(action_1) {
22
+ export default function withRetries(action_1) {
26
23
  return __awaiter(this, arguments, void 0, function* (action, maxRetries = 3, interval = 300) {
27
24
  /* eslint-disable no-await-in-loop */
28
25
  for (let n = 1;; ++n) {
@@ -32,7 +29,7 @@ function withRetries(action_1) {
32
29
  }
33
30
  catch (error) {
34
31
  if (n < maxRetries)
35
- yield (0, time_1.timer)(interval);
32
+ yield timer(interval);
36
33
  else
37
34
  throw error;
38
35
  }
package/jest.config.js CHANGED
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  collectCoverage: true,
3
3
  coverageDirectory: '__coverage__',
4
4
  testPathIgnorePatterns: [
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "@dr.pogodin/js-utils",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Collection of JavaScript (TypeScript) utilities.",
5
5
  "main": "build/index",
6
- "react-native": "src/index",
7
- "source": "src/index",
6
+ "type": "module",
8
7
  "types": "build/index.d.ts",
9
8
  "scripts": {
10
9
  "build": "rimraf build && tsc",
@@ -31,8 +30,8 @@
31
30
  },
32
31
  "homepage": "https://github.com/birdofpreyru/js-utils#readme",
33
32
  "devDependencies": {
34
- "@babel/core": "^7.26.0",
35
- "@babel/preset-env": "^7.26.0",
33
+ "@babel/core": "^7.26.9",
34
+ "@babel/preset-env": "^7.26.9",
36
35
  "@babel/preset-typescript": "^7.26.0",
37
36
  "@tsconfig/recommended": "^1.0.8",
38
37
  "@types/jest": "^29.5.14",
@@ -40,12 +39,12 @@
40
39
  "eslint": "^8.57.1",
41
40
  "eslint-config-airbnb-base": "^15.0.0",
42
41
  "eslint-config-airbnb-typescript": "^18.0.0",
43
- "eslint-import-resolver-typescript": "^3.7.0",
42
+ "eslint-import-resolver-typescript": "^3.8.3",
44
43
  "eslint-plugin-import": "^2.31.0",
45
44
  "jest": "^29.7.0",
46
45
  "rimraf": "^6.0.1",
47
- "tstyche": "^3.2.0",
48
- "typescript": "^5.7.2",
49
- "typescript-eslint": "^8.18.2"
46
+ "tstyche": "^3.5.0",
47
+ "typescript": "^5.7.3",
48
+ "typescript-eslint": "^8.24.1"
50
49
  }
51
50
  }
package/tsconfig.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "include": ["src/**/*"],
4
4
  "compilerOptions": {
5
5
  "declaration": true,
6
+ "module": "Preserve",
6
7
  "outDir": "build"
7
8
  }
8
9
  }
package/src/Barrier.ts DELETED
@@ -1,116 +0,0 @@
1
- export type Executor<T> = ConstructorParameters<typeof Promise<T>>[0];
2
-
3
- type Resolver<T> = Parameters<Executor<T>>[0];
4
- type Rejecter = Parameters<Executor<unknown>>[1];
5
-
6
- enum STATE {
7
- PENDING = 'PENDING',
8
- REJECTED = 'REJECTED',
9
- RESOLVED = 'RESOLVED',
10
- }
11
-
12
- /**
13
- * Barrier is just a Promise which has resolve and reject exposed as instance
14
- * methods.
15
- *
16
- * It has two generic arguments T and TR which correspond to the argument of
17
- * the .resolve() method, and to the value resolved by the promise (barrier).
18
- * For a simple barrier TR equals to T, however for barriers created via .then()
19
- * chain, T corresponds to the argument of the original barrier, and TR to
20
- * the value resolved by the latest promise in the chain. Consider this:
21
- *
22
- * const b = new Barrier<string>();
23
- * b.resolve('result');
24
- * const s = await b; // `s` has `string` type, and equals "result".
25
- *
26
- * const b = (new Barrier<string>()).then((s) => s.length);
27
- * b.resolve('result'); // Chained barrier exposes .resolve() method of
28
- * // the first barrier in the chain, which expects
29
- * // `string` arugment (T), but the chained barrier
30
- * // resolves to `number` (TR).
31
- * const n = await b; // `n` has `number` type, and equals 6.
32
- *
33
- * Docs: https://dr.pogodin.studio/docs/react-utils/docs/api/classes/Barrier
34
- */
35
- export default class Barrier<T = unknown, TR = T> extends Promise<TR> {
36
- private p_resolve: Resolver<T>;
37
-
38
- private p_reject: Rejecter;
39
-
40
- private p_state = STATE.PENDING;
41
-
42
- constructor(executor?: Executor<TR>) {
43
- let resolveRef: Resolver<TR>;
44
- let rejectRef: Rejecter;
45
-
46
- super((resolve, reject) => {
47
- // Note: Enforcing `void` return type because of the BEWARE note below.
48
- resolveRef = (value: TR | PromiseLike<TR>): void => {
49
- resolve(value);
50
- this.p_state = STATE.RESOLVED;
51
-
52
- // BEWARE: Don't try to return `this` here, it will easily cause
53
- // infinite loops in React Native, which are extremely difficult
54
- // to troubleshoot (I wasn't able to figure out, are they due to
55
- // internal Promise implementation in RN, or because of some bad
56
- // patterns in the host code).
57
- };
58
-
59
- // Note: Enforcing `void` return type because of the BEWARE note below.
60
- rejectRef = (reason?: any): void => {
61
- reject(reason);
62
- this.p_state = STATE.REJECTED;
63
- };
64
-
65
- if (executor) executor(resolveRef, rejectRef);
66
- });
67
-
68
- // NOTE: We assume, the only scenario where TR is not equal T is when
69
- // the Barrier is constructed by a .then() call on a "parent" barrier,
70
- // and in that scenario .then() itself will replace .p_resolve by another
71
- // resolver immediately after this constructor returns.
72
- this.p_resolve = resolveRef! as Resolver<T>;
73
-
74
- this.p_reject = rejectRef!;
75
- }
76
-
77
- get resolve() {
78
- return (arg: Parameters<Resolver<T>>[0]) => {
79
- this.p_resolve(arg);
80
- return this;
81
- };
82
- }
83
-
84
- get reject() {
85
- return (arg: Parameters<Rejecter>[0]) => {
86
- this.p_reject(arg);
87
- return this;
88
- };
89
- }
90
-
91
- get resolved() { return this.p_state === STATE.RESOLVED; }
92
-
93
- get rejected() { return this.p_state === STATE.REJECTED; }
94
-
95
- get settled() { return this.p_state !== STATE.PENDING; }
96
-
97
- catch<TR1>(
98
- onRejected?: ((reason: any) => TR1 | PromiseLike<TR1>) | null,
99
- ): Barrier<T, TR1> {
100
- return <Barrier<T, TR1>> super.catch(onRejected);
101
- }
102
-
103
- finally(onFinally?: (() => void) | null): Barrier<TR> {
104
- return <Barrier<TR>> super.finally(onFinally);
105
- }
106
-
107
- then<TR1, TR2>(
108
- onFulfilled?: ((value: TR) => TR1 | PromiseLike<TR1>) | null,
109
- onRejected?: ((reason: any) => TR2 | PromiseLike<TR2>) | null,
110
- ): Barrier<T, TR1 | TR2> {
111
- const res = <Barrier<T, TR1 | TR2>> super.then(onFulfilled, onRejected);
112
- res.p_resolve = this.resolve;
113
- res.p_reject = this.reject;
114
- return res;
115
- }
116
- }
package/src/Emitter.ts DELETED
@@ -1,57 +0,0 @@
1
- export type Listener<T extends unknown[] = unknown[]> = (...args: T) => void;
2
-
3
- /**
4
- * Simple listeneable data Emitter.
5
- */
6
- export class Emitter<T extends unknown[] = unknown[]> {
7
- private p_listeners: Listener<T>[] = [];
8
-
9
- /**
10
- * Returns "true" if any listener is connected; "false" otherwise.
11
- * @return {boolean}
12
- */
13
- get hasListeners(): boolean {
14
- return !!this.p_listeners.length;
15
- }
16
-
17
- get listeners(): ReadonlyArray<Listener<T>> { return this.p_listeners; }
18
-
19
- /**
20
- * Adds `listener` if it is not already connected.
21
- * @param {function} listener
22
- * @return {function} Unsubscribe function.
23
- */
24
- addListener(listener: Listener<T>): () => void {
25
- if (!this.p_listeners.includes(listener)) {
26
- this.p_listeners.push(listener);
27
- }
28
- return () => this.removeListener(listener);
29
- }
30
-
31
- /**
32
- * Calls every connected listener with the given arguments.
33
- * @param args
34
- */
35
- emit(...args: T) {
36
- const listeners = this.p_listeners.slice();
37
- for (let i = 0; i < listeners.length; ++i) {
38
- listeners[i](...args);
39
- }
40
- }
41
-
42
- /**
43
- * Removes all connected listeners.
44
- */
45
- removeAllListeners() {
46
- this.p_listeners = [];
47
- }
48
-
49
- /**
50
- * Removes specified `listener`, if connected.
51
- * @param listener
52
- */
53
- removeListener(listener: Listener<T>) {
54
- const idx = this.p_listeners.indexOf(listener);
55
- if (idx >= 0) this.p_listeners.splice(idx, 1);
56
- }
57
- }
package/src/Semaphore.ts DELETED
@@ -1,78 +0,0 @@
1
- import Barrier from './Barrier';
2
-
3
- /**
4
- * Implements a simple semaphore for async code logic.
5
- */
6
- export default class Semaphore {
7
- constructor(ready = false) {
8
- this.p_ready = !!ready;
9
- }
10
-
11
- get ready() { return this.p_ready; }
12
-
13
- setReady(ready: boolean) {
14
- const bool = !!ready;
15
- if (this.p_ready !== bool) {
16
- this.p_ready = bool;
17
- if (bool && !this.p_draining && this.p_queue.length) {
18
- this.p_drainQueue();
19
- }
20
- }
21
- }
22
-
23
- /**
24
- * Waits until the semaphore is ready, and marks it as non-ready (seizes it).
25
- * @return {Promise}
26
- */
27
- async seize() {
28
- return this.waitReady(true);
29
- }
30
-
31
- async waitReady(seize = false) {
32
- if (!this.p_ready || this.p_queue.length) {
33
- const barrier = new Barrier<void>();
34
- this.p_queue.push(barrier);
35
- await barrier;
36
- if (seize) this.p_ready = false;
37
- this.p_drainLock!.resolve();
38
- } else if (seize) this.p_ready = false;
39
- }
40
-
41
- // Private members below this point.
42
-
43
- /**
44
- * If semaphore is ready, it releases the next barrier in the queue, if any,
45
- * and reschedules itself for a call in the next event loop iteration.
46
- * Otherwise, it breaks the queue draining loop, which will be restarted
47
- * the next time the semaphore is set ready.
48
- */
49
- async p_drainQueue() {
50
- this.p_draining = true;
51
- while (this.p_ready && this.p_queue.length) {
52
- this.p_drainLock = new Barrier();
53
- this.p_queue[0].resolve();
54
- await this.p_drainLock; // eslint-disable-line no-await-in-loop
55
- this.p_queue.shift();
56
- }
57
- this.p_draining = false;
58
- this.p_drainLock = null;
59
- }
60
-
61
- // "true" when the drain queue process is running (and thus no need to start
62
- // a new one).
63
- private p_draining = false;
64
-
65
- // Each time a Promise from drain queue is resolved this drainLock is set
66
- // to block further queue draining until the promise resolution handler
67
- // (.seize() or .waitReady()) unlocks it, thus confirming it is fine
68
- // to continue the draining. This is specifically important for .seize(),
69
- // which should have a chance to switch semaphore state to non-ready prior
70
- // to next Promise in the queue being unlocked.
71
- private p_drainLock: Barrier<void> | null = null;
72
-
73
- // The array of barriers set for each async code flow awaiting for
74
- // the Semaphore to become ready.
75
- private p_queue: Barrier<void>[] = [];
76
-
77
- private p_ready: boolean;
78
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export { default as Barrier } from './Barrier';
2
- export * from './Emitter';
3
- export { default as Semaphore } from './Semaphore';
4
- export * from './time';
5
- export { default as withRetries } from './withRetries';
package/src/time.ts DELETED
@@ -1,77 +0,0 @@
1
- import Barrier, { type Executor } from './Barrier';
2
-
3
- // This is not very elegant, but as of now TypeScript does not support type
4
- // arithmetic, thus we can't have constants assigned like `MIN_MS = 60 * SEC_MS`
5
- // and have the result type to be 60000 (number literal), it would be just
6
- // the generic number type.
7
- export const SEC_MS = 1000;
8
- export const MIN_MS = 60000; // 60 * SEC_MS
9
- export const HOUR_MS = 3600000; // 60 * MIN_MS
10
- export const DAY_MS = 86400000; // 24 * HOUR_MS
11
- export const YEAR_MS = 31536000000; // 365 * DAY_MS
12
-
13
- // TODO: Ok, as we have ended up with a Timer class, mostly to achieve a good
14
- // TypeScript typing for timer() function, it makes sense to expose the class
15
- // from the library as well, and it should be documented later.
16
- export class Timer<T> extends Barrier<void, T> {
17
- private p_abort: () => void;
18
-
19
- private p_timeout?: number;
20
-
21
- get abort(): () => void { return this.p_abort; }
22
-
23
- get timeout(): number | undefined { return this.p_timeout; }
24
-
25
- /**
26
- * Creates a new, non-initialized instance of Timer. Call .init() method
27
- * to actually initialize and launch the timer.
28
- *
29
- * NOTE: Although it might be tempting to accept `timeout` value as
30
- * a constructor's argument, it won't work well, because Timer is an
31
- * extension of Promise (via Barrier), and the way Promises works (in
32
- * particular their .then() method, which internally calls constructor()
33
- * with special executor) does not play along with initalization depending
34
- * on custom parameters done in constructor().
35
- *
36
- * @param executor
37
- */
38
- constructor(executor?: Executor<T>) {
39
- super(executor);
40
- this.p_abort = () => {};
41
- }
42
-
43
- init(timeout: number): Timer<T> {
44
- if (this.p_timeout !== undefined) {
45
- throw Error('This Timer is initialized already');
46
- }
47
- this.p_timeout = timeout;
48
- if (timeout > 0) {
49
- const id = setTimeout(super.resolve.bind(this), timeout);
50
- this.p_abort = () => clearTimeout(id);
51
- } else {
52
- super.resolve();
53
- }
54
- return this;
55
- }
56
-
57
- then<TR1, TR2>(
58
- onFulfilled?: ((value: T) => TR1 | PromiseLike<TR1>) | null,
59
- onRejected?: ((reason: any) => TR2 | PromiseLike<TR2>) | null,
60
- ): Timer<TR1 | TR2> {
61
- const res = <Timer<TR1 | TR2>> super.then(onFulfilled, onRejected);
62
- if (this.timeout !== undefined) res.init(this.timeout);
63
- return res;
64
- }
65
- }
66
-
67
- /**
68
- * Creates a Promise, which resolves after the given timeout.
69
- * @param {number} timeout Timeout [ms].
70
- * @return {Barrier} Resolves after the timeout. It has additional
71
- * .abort() method attached, which cancels the pending timer resolution
72
- * (without resolving or rejecting the barrier).
73
- */
74
- export function timer(timeout: number): Timer<void> {
75
- const t = new Timer<void>();
76
- return t.init(timeout);
77
- }
@@ -1,30 +0,0 @@
1
- import { timer } from './time';
2
-
3
- /**
4
- * Attempts to perform the given async `action` up to `maxRetries` times with
5
- * the specified `interval`, stopping at the first successful (non-throwing)
6
- * execution.
7
- * @param action
8
- * @param maxRetries Optional. The maximum number of re-tries. Defaults 3.
9
- * @param interval Optional. The interval between re-tries (in milliseconds).
10
- * Defaults to 300ms.
11
- * @returns Resolves to the result of the successful `action` execution;
12
- * or rejects with the error from the last faileda attempt.
13
- */
14
- export default async function withRetries<T>(
15
- action: () => T,
16
- maxRetries = 3,
17
- interval = 300,
18
- ): Promise<Awaited<T>> {
19
- /* eslint-disable no-await-in-loop */
20
- for (let n = 1; ; ++n) {
21
- try {
22
- const res = action();
23
- return res instanceof Promise ? await res : (res as Awaited<T>);
24
- } catch (error) {
25
- if (n < maxRetries) await timer(interval);
26
- else throw error;
27
- }
28
- }
29
- /* eslint-enable no-await-in-loop */
30
- }