async-queue-runner 0.14.0 → 0.16.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/_cjs/action.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Action = void 0;
3
+ exports.lockingClassFactory = exports.LockingAction = exports.Action = void 0;
4
4
  class Action {
5
5
  delay = 0;
6
6
  constructor(opts = {}) {
@@ -9,3 +9,19 @@ class Action {
9
9
  }
10
10
  }
11
11
  exports.Action = Action;
12
+ class LockingAction extends Action {
13
+ locking = true;
14
+ scope = null;
15
+ }
16
+ exports.LockingAction = LockingAction;
17
+ function lockingClassFactory(scope) {
18
+ if (typeof scope !== 'string') {
19
+ throw new TypeError('scope must be a string');
20
+ }
21
+ class NoName extends LockingAction {
22
+ scope = scope;
23
+ }
24
+ Object.defineProperty(NoName, 'name', { value: `${LockingAction.constructor.name}(${scope})` });
25
+ return NoName;
26
+ }
27
+ exports.lockingClassFactory = lockingClassFactory;
package/_cjs/queue.js CHANGED
@@ -14,6 +14,7 @@ class AsyncQueue {
14
14
  queue = [];
15
15
  end;
16
16
  logger;
17
+ locker;
17
18
  loopAction = false;
18
19
  context = {
19
20
  push: (actions) => this.push(actions),
@@ -31,6 +32,7 @@ class AsyncQueue {
31
32
  this.name = opts.name;
32
33
  this.end = opts.end || (() => { });
33
34
  this.logger = opts.logger || logger;
35
+ this.locker = opts.lockingContext;
34
36
  }
35
37
  async delay(timeout) {
36
38
  return new Promise((res) => setTimeout(res, timeout));
@@ -47,7 +49,18 @@ class AsyncQueue {
47
49
  }
48
50
  const item = this.queue.shift();
49
51
  const action = this.processQueueItem(item);
52
+ const isLocking = action.locking;
53
+ const scope = action.scope;
54
+ if (isLocking) {
55
+ if (this.locker.isLocked(scope)) {
56
+ await this.locker.wait(scope);
57
+ }
58
+ this.locker.lock(scope);
59
+ }
50
60
  await this.iterate(action);
61
+ if (isLocking) {
62
+ this.locker.unlock(scope);
63
+ }
51
64
  }
52
65
  this.end();
53
66
  }
package/_cjs/runner.js CHANGED
@@ -2,14 +2,57 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.QueueRunner = void 0;
4
4
  const queue_js_1 = require("./queue.js");
5
+ function reversePromiseFactory() {
6
+ let resolve = (value) => { };
7
+ const promise = new Promise((res) => {
8
+ resolve = res;
9
+ });
10
+ return {
11
+ promise,
12
+ resolve,
13
+ };
14
+ }
5
15
  class QueueRunner {
6
16
  queues = new Map();
7
17
  listeners = [];
8
18
  logger;
19
+ locking;
9
20
  constructor(opts = {}) {
10
21
  if (opts.logger) {
11
22
  this.logger = opts.logger;
12
23
  }
24
+ this.locking = this.preparteLockingContext();
25
+ }
26
+ preparteLockingContext() {
27
+ const lockedScopes = new Map();
28
+ const unlock = (scope) => {
29
+ if (!lockedScopes.has(scope)) {
30
+ return;
31
+ }
32
+ lockedScopes.get(scope).resolve();
33
+ lockedScopes.delete(scope);
34
+ };
35
+ const wait = (scope) => {
36
+ if (!lockedScopes.has(scope)) {
37
+ return Promise.resolve();
38
+ }
39
+ return lockedScopes.get(scope).promise;
40
+ };
41
+ const lock = (scope) => {
42
+ if (lockedScopes.has(scope)) {
43
+ throw new Error(`scope "${scope}" is already locked`);
44
+ }
45
+ lockedScopes.set(scope, reversePromiseFactory());
46
+ };
47
+ const isLocked = (scope) => {
48
+ return lockedScopes.has(scope);
49
+ };
50
+ return {
51
+ isLocked,
52
+ lock,
53
+ unlock,
54
+ wait,
55
+ };
13
56
  }
14
57
  add(actions, context = {}, name = this.getName()) {
15
58
  const queue = new queue_js_1.AsyncQueue({
@@ -19,6 +62,7 @@ class QueueRunner {
19
62
  this.endEvent(name);
20
63
  },
21
64
  logger: this.logger,
65
+ lockingContext: this.locking,
22
66
  });
23
67
  this.queues.set(name, queue);
24
68
  queue.run(context);
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logger = void 0;
4
+ exports.logger = {
5
+ info: function (message) { },
6
+ setContext: function (context) { },
7
+ error: function (e) { }
8
+ };
package/_esm/action.js CHANGED
@@ -1,11 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Action = void 0;
4
- class Action {
1
+ export class Action {
5
2
  delay = 0;
6
3
  constructor(opts = {}) {
7
4
  if (opts.delay)
8
5
  this.delay = opts.delay;
9
6
  }
10
7
  }
11
- exports.Action = Action;
8
+ export class LockingAction extends Action {
9
+ locking = true;
10
+ scope = null;
11
+ }
12
+ export function lockingClassFactory(scope) {
13
+ if (typeof scope !== 'string') {
14
+ throw new TypeError('scope must be a string');
15
+ }
16
+ class NoName extends LockingAction {
17
+ scope = scope;
18
+ }
19
+ Object.defineProperty(NoName, 'name', { value: `${LockingAction.constructor.name}(${scope})` });
20
+ return NoName;
21
+ }
package/_esm/index.js CHANGED
@@ -1,11 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.util = exports.Action = exports.AsyncQueue = exports.QueueRunner = void 0;
4
- var runner_js_1 = require("./runner.js");
5
- Object.defineProperty(exports, "QueueRunner", { enumerable: true, get: function () { return runner_js_1.QueueRunner; } });
6
- var queue_js_1 = require("./queue.js");
7
- Object.defineProperty(exports, "AsyncQueue", { enumerable: true, get: function () { return queue_js_1.AsyncQueue; } });
8
- var action_js_1 = require("./action.js");
9
- Object.defineProperty(exports, "Action", { enumerable: true, get: function () { return action_js_1.Action; } });
10
- var utils_js_1 = require("./utils.js");
11
- Object.defineProperty(exports, "util", { enumerable: true, get: function () { return utils_js_1.util; } });
1
+ export { QueueRunner } from './runner.js';
2
+ export { AsyncQueue } from './queue.js';
3
+ export { Action } from './action.js';
4
+ export { util } from './utils.js';
package/_esm/queue.js CHANGED
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AsyncQueue = void 0;
4
- const action_js_1 = require("./action.js");
1
+ import { Action } from './action.js';
5
2
  const logger = {
6
3
  info(message) {
7
4
  console.log(message);
@@ -9,11 +6,12 @@ const logger = {
9
6
  setContext(context) { },
10
7
  error(e) { console.error(e); },
11
8
  };
12
- class AsyncQueue {
9
+ export class AsyncQueue {
13
10
  name = 'default queue name';
14
11
  queue = [];
15
12
  end;
16
13
  logger;
14
+ locker;
17
15
  loopAction = false;
18
16
  context = {
19
17
  push: (actions) => this.push(actions),
@@ -31,6 +29,7 @@ class AsyncQueue {
31
29
  this.name = opts.name;
32
30
  this.end = opts.end || (() => { });
33
31
  this.logger = opts.logger || logger;
32
+ this.locker = opts.lockingContext;
34
33
  }
35
34
  async delay(timeout) {
36
35
  return new Promise((res) => setTimeout(res, timeout));
@@ -47,7 +46,18 @@ class AsyncQueue {
47
46
  }
48
47
  const item = this.queue.shift();
49
48
  const action = this.processQueueItem(item);
49
+ const isLocking = action.locking;
50
+ const scope = action.scope;
51
+ if (isLocking) {
52
+ if (this.locker.isLocked(scope)) {
53
+ await this.locker.wait(scope);
54
+ }
55
+ this.locker.lock(scope);
56
+ }
50
57
  await this.iterate(action);
58
+ if (isLocking) {
59
+ this.locker.unlock(scope);
60
+ }
51
61
  }
52
62
  this.end();
53
63
  }
@@ -67,11 +77,10 @@ class AsyncQueue {
67
77
  this.queue.unshift(...actions);
68
78
  }
69
79
  processQueueItem(item) {
70
- if (item instanceof action_js_1.Action) {
80
+ if (item instanceof Action) {
71
81
  return item;
72
82
  }
73
83
  else
74
84
  return new item;
75
85
  }
76
86
  }
77
- exports.AsyncQueue = AsyncQueue;
package/_esm/runner.js CHANGED
@@ -1,24 +1,65 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.QueueRunner = void 0;
4
- const queue_js_1 = require("./queue.js");
5
- class QueueRunner {
1
+ import { AsyncQueue } from "./queue.js";
2
+ function reversePromiseFactory() {
3
+ let resolve = (value) => { };
4
+ const promise = new Promise((res) => {
5
+ resolve = res;
6
+ });
7
+ return {
8
+ promise,
9
+ resolve,
10
+ };
11
+ }
12
+ export class QueueRunner {
6
13
  queues = new Map();
7
14
  listeners = [];
8
15
  logger;
16
+ locking;
9
17
  constructor(opts = {}) {
10
18
  if (opts.logger) {
11
19
  this.logger = opts.logger;
12
20
  }
21
+ this.locking = this.preparteLockingContext();
22
+ }
23
+ preparteLockingContext() {
24
+ const lockedScopes = new Map();
25
+ const unlock = (scope) => {
26
+ if (!lockedScopes.has(scope)) {
27
+ return;
28
+ }
29
+ lockedScopes.get(scope).resolve();
30
+ lockedScopes.delete(scope);
31
+ };
32
+ const wait = (scope) => {
33
+ if (!lockedScopes.has(scope)) {
34
+ return Promise.resolve();
35
+ }
36
+ return lockedScopes.get(scope).promise;
37
+ };
38
+ const lock = (scope) => {
39
+ if (lockedScopes.has(scope)) {
40
+ throw new Error(`scope "${scope}" is already locked`);
41
+ }
42
+ lockedScopes.set(scope, reversePromiseFactory());
43
+ };
44
+ const isLocked = (scope) => {
45
+ return lockedScopes.has(scope);
46
+ };
47
+ return {
48
+ isLocked,
49
+ lock,
50
+ unlock,
51
+ wait,
52
+ };
13
53
  }
14
54
  add(actions, context = {}, name = this.getName()) {
15
- const queue = new queue_js_1.AsyncQueue({
55
+ const queue = new AsyncQueue({
16
56
  name, actions,
17
57
  end: () => {
18
58
  this.queues.delete(name);
19
59
  this.endEvent(name);
20
60
  },
21
61
  logger: this.logger,
62
+ lockingContext: this.locking,
22
63
  });
23
64
  this.queues.set(name, queue);
24
65
  queue.run(context);
@@ -35,4 +76,3 @@ class QueueRunner {
35
76
  return Math.random().toString(36).substring(2, 10);
36
77
  }
37
78
  }
38
- exports.QueueRunner = QueueRunner;
@@ -0,0 +1,5 @@
1
+ export const logger = {
2
+ info: function (message) { },
3
+ setContext: function (context) { },
4
+ error: function (e) { }
5
+ };
package/_esm/types.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
package/_esm/utils.js CHANGED
@@ -1,21 +1,17 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.util = exports.Delay = void 0;
4
- const action_js_1 = require("./action.js");
5
- class Delay extends action_js_1.Action {
1
+ import { Action } from "./action.js";
2
+ export class Delay extends Action {
6
3
  async execute() {
7
4
  return new Promise(res => {
8
5
  setTimeout(res, this.delay);
9
6
  });
10
7
  }
11
8
  }
12
- exports.Delay = Delay;
13
- exports.util = {
9
+ export const util = {
14
10
  delay(timeout) {
15
11
  return new Delay({ delay: timeout });
16
12
  },
17
13
  if(condition, branches) {
18
- class IfAction extends action_js_1.Action {
14
+ class IfAction extends Action {
19
15
  async execute(context) {
20
16
  const result = await condition(context);
21
17
  if (result) {
@@ -29,7 +25,7 @@ exports.util = {
29
25
  return new IfAction();
30
26
  },
31
27
  valid(validator, actions) {
32
- class Validator extends action_js_1.Action {
28
+ class Validator extends Action {
33
29
  async execute(context) {
34
30
  const valid = await validator(context);
35
31
  if (valid) {
@@ -1,4 +1,4 @@
1
- import { IAction, QueueContext } from "./types.js";
1
+ import { IAction, ILockingAction, QueueContext } from "./types.js";
2
2
  export type Options = {
3
3
  delay?: number;
4
4
  };
@@ -7,3 +7,13 @@ export declare abstract class Action<C> implements IAction {
7
7
  constructor(opts?: Options);
8
8
  abstract execute(context: C & QueueContext): Promise<void>;
9
9
  }
10
+ export declare abstract class LockingAction<C> extends Action<C> implements ILockingAction {
11
+ locking: boolean;
12
+ scope: string | null;
13
+ }
14
+ export declare function lockingClassFactory<C>(scope: string): abstract new (opts?: Options) => {
15
+ scope: string;
16
+ locking: boolean;
17
+ delay: number;
18
+ execute(context: C & QueueContext): Promise<void>;
19
+ };
package/_types/queue.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { LockingContext } from './runner.js';
1
2
  import { IAction, QueueAction, QueueContext } from './types.js';
2
3
  export interface ILogger {
3
4
  info: (message: string) => void;
@@ -9,12 +10,14 @@ export type QueueOpts = {
9
10
  name: string;
10
11
  end?: () => void;
11
12
  logger?: ILogger;
13
+ lockingContext: LockingContext;
12
14
  };
13
15
  export declare class AsyncQueue {
14
16
  name: string;
15
17
  queue: QueueAction[];
16
18
  end: () => void;
17
19
  logger: ILogger;
20
+ locker: LockingContext;
18
21
  loopAction: boolean;
19
22
  context: QueueContext;
20
23
  constructor(opts: QueueOpts);
@@ -4,11 +4,19 @@ export type EndListener = (name: string, size: number) => void;
4
4
  export type RunnerOpts = {
5
5
  logger?: ILogger;
6
6
  };
7
+ export type LockingContext = {
8
+ isLocked: (scope: string) => boolean;
9
+ lock: (scope: string) => void;
10
+ unlock: (scope: string) => void;
11
+ wait: (scope: string) => Promise<unknown>;
12
+ };
7
13
  export declare class QueueRunner {
8
14
  queues: Map<any, any>;
9
15
  listeners: EndListener[];
10
16
  logger?: ILogger;
17
+ locking: LockingContext;
11
18
  constructor(opts?: RunnerOpts);
19
+ preparteLockingContext(): LockingContext;
12
20
  add(actions: QueueAction[], context?: object, name?: string): void;
13
21
  addEndListener(listener: EndListener): void;
14
22
  endEvent(name: string): void;
@@ -0,0 +1,2 @@
1
+ import { ILogger } from "./queue";
2
+ export declare const logger: ILogger;
package/_types/types.d.ts CHANGED
@@ -2,8 +2,12 @@ export interface IAction {
2
2
  delay: number;
3
3
  execute: (context: any) => Promise<void>;
4
4
  }
5
+ export interface ILockingAction {
6
+ locking: boolean;
7
+ scope: string | null;
8
+ }
5
9
  export type ActionClass = new (...args: any[]) => IAction;
6
- export type QueueAction = IAction | ActionClass;
10
+ export type QueueAction = IAction | ActionClass | Partial<ILockingAction>;
7
11
  export type Branches = {
8
12
  then: QueueAction[];
9
13
  else?: QueueAction[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "async-queue-runner",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Library to run in parallel extendable queue of tasks",
5
5
  "scripts": {
6
6
  "compile": "tsc --project ./tsconfig.json",