clever-queue 0.1.1 → 0.2.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/.gitlab-ci.yml +80 -0
- package/README.md +66 -6
- package/dist/engine/index.d.ts +14 -10
- package/dist/engine/index.js +45 -46
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/interfaces.d.ts +3 -2
- package/dist/engine/interfaces.js.map +1 -1
- package/dist/helpers/id.d.ts +2 -0
- package/dist/helpers/id.js +7 -0
- package/dist/helpers/id.js.map +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +19 -8
- package/dist/helpers/index.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +25 -15
- package/dist/index.js.map +1 -1
- package/dist/{queue → queues}/index.d.ts +8 -5
- package/dist/{queue → queues}/index.js +27 -33
- package/dist/queues/index.js.map +1 -0
- package/dist/{queue → queues}/interfaces.d.ts +9 -4
- package/dist/{queue → queues}/interfaces.js +4 -2
- package/dist/queues/interfaces.js.map +1 -0
- package/dist/{runner → runners}/index.d.ts +7 -3
- package/dist/{runner → runners}/index.js +33 -15
- package/dist/runners/index.js.map +1 -0
- package/dist/{runner → runners}/interfaces.d.ts +2 -1
- package/dist/{runner → runners}/interfaces.js +1 -1
- package/dist/runners/interfaces.js.map +1 -0
- package/dist/{task → tasks}/index.d.ts +8 -2
- package/dist/{task → tasks}/index.js +35 -16
- package/dist/tasks/index.js.map +1 -0
- package/dist/{task → tasks}/interfaces.d.ts +2 -1
- package/dist/{task → tasks}/interfaces.js +4 -0
- package/dist/tasks/interfaces.js.map +1 -0
- package/documentation/demo_1E_1Q_1R_4T.svg +1 -0
- package/documentation/demo_1E_1Q_2R_4T.svg +1 -0
- package/documentation/demo_1E_2Q_1R_8T.svg +1 -0
- package/eslint.config.mjs +68 -68
- package/examples/package-lock.json +19 -0
- package/examples/package.json +15 -0
- package/examples/src/demo_1E_1Q_1R_4T.ts +37 -0
- package/examples/src/demo_1E_1Q_2R_4T.ts +36 -0
- package/examples/src/demo_1E_2Q_1R_8T.ts +40 -0
- package/examples/src/example00.ts +16 -0
- package/examples/src/helpers/animations.ts +295 -0
- package/examples/src/helpers/index.ts +2 -0
- package/examples/src/helpers/myAsyncTaskToExecute.ts +6 -0
- package/examples/src/helpers/svg.ts +155 -0
- package/examples/src/tsconfig.json +24 -0
- package/package.json +65 -55
- package/src/engine/index.ts +37 -48
- package/src/engine/interfaces.ts +3 -2
- package/src/helpers/id.ts +4 -0
- package/src/helpers/index.ts +1 -0
- package/src/index.ts +7 -7
- package/src/{queue → queues}/index.ts +11 -28
- package/src/{queue → queues}/interfaces.ts +4 -3
- package/src/{runner → runners}/index.ts +17 -12
- package/src/{runner → runners}/interfaces.ts +2 -1
- package/src/{task → tasks}/index.ts +18 -11
- package/src/{task → tasks}/interfaces.ts +6 -1
- package/test/issues/00001.mjs +35 -0
- package/test/miscellaneous/test.mjs +13 -13
- package/test/units/engine.mjs +5 -5
- package/test/units/queue.mjs +4 -4
- package/test/units/task.mjs +6 -6
- package/dist/errors.d.ts +0 -13
- package/dist/errors.js +0 -42
- package/dist/errors.js.map +0 -1
- package/dist/queue/index.js.map +0 -1
- package/dist/queue/interfaces.js.map +0 -1
- package/dist/runner/index.js.map +0 -1
- package/dist/runner/interfaces.js.map +0 -1
- package/dist/task/index.js.map +0 -1
- package/dist/task/interfaces.js.map +0 -1
- package/exemples/index01.js +0 -116
- package/exemples/index01.ts +0 -98
- package/exemples/index02.mjs +0 -13
- package/exemples/tsconfig.json +0 -24
package/package.json
CHANGED
|
@@ -1,55 +1,65 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "clever-queue",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Queuing system for promises that handle concurring, throttling, weighting and prioritizing in a clever fashion.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "npx tsc -p src/ --declaration",
|
|
8
|
-
"build:
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"test
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"queue"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"eslint": "^
|
|
44
|
-
"eslint
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "clever-queue",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Queuing system for promises that handle concurring, throttling, weighting and prioritizing in a clever fashion.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "npx tsc -p src/ --declaration",
|
|
8
|
+
"build:examples": "npx tsc -p examples/src/",
|
|
9
|
+
"build:watch": "npx tsc -p src/ -w",
|
|
10
|
+
"prestart": "npx npm run build",
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"debug": "node --nolazy --inspect-brk=9229 dist/index.js",
|
|
13
|
+
"test": "node --test ./test/**/*.mjs",
|
|
14
|
+
"test:junit": "node --test --test-reporter=junit --test-reporter-destination=junit.xml ./test/**/*.mjs",
|
|
15
|
+
"test:unit:watch": "node --test --watch ./test/units/*.mjs",
|
|
16
|
+
"semantic-release": "npx semantic-release"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://gitlab.com/job-so/clever-queue.git"
|
|
21
|
+
},
|
|
22
|
+
"release": {
|
|
23
|
+
"branches": [
|
|
24
|
+
"v0"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"queue",
|
|
29
|
+
"clever",
|
|
30
|
+
"concurrency",
|
|
31
|
+
"throttling",
|
|
32
|
+
"priority",
|
|
33
|
+
"weight",
|
|
34
|
+
"promise"
|
|
35
|
+
],
|
|
36
|
+
"author": "Olivier GROSJEANNE",
|
|
37
|
+
"license": "ISC",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://gitlab.com/job-so/clever-queue/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://gitlab.com/job-so/clever-queue#readme",
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@eslint/eslintrc": "^3.1",
|
|
44
|
+
"@eslint/js": "^9.8",
|
|
45
|
+
"@opentelemetry/api": "^1.9.0",
|
|
46
|
+
"@opentelemetry/sdk-metrics": "^1.26",
|
|
47
|
+
"@opentelemetry/sdk-node": "^0.53",
|
|
48
|
+
"@opentelemetry/sdk-trace-node": "^1.26",
|
|
49
|
+
"@types/eslint__js": "^8.42",
|
|
50
|
+
"@types/node": "^22.1",
|
|
51
|
+
"eslint": "^9.8",
|
|
52
|
+
"eslint-config-prettier": "^9.1",
|
|
53
|
+
"eslint-plugin-node": "^11.1",
|
|
54
|
+
"eslint-plugin-prettier": "^5.1",
|
|
55
|
+
"eslint-plugin-promise": "^6.4",
|
|
56
|
+
"eslint-plugin-security": "^3.0",
|
|
57
|
+
"eslint-plugin-unicorn": "^54.0",
|
|
58
|
+
"fast-xml-parser": "^5.0.9",
|
|
59
|
+
"globals": "^15.9",
|
|
60
|
+
"node": "^24.12",
|
|
61
|
+
"semantic-release": "^25.0.2",
|
|
62
|
+
"typescript": "^5.9",
|
|
63
|
+
"typescript-eslint": "^8.50"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/engine/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// clever-queue
|
|
2
2
|
|
|
3
3
|
import * as _ from "../helpers";
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
4
|
+
import * as cqQueue from "../queues";
|
|
5
|
+
import * as cqRunner from "../runners";
|
|
6
|
+
import * as cqTask from "../tasks";
|
|
7
7
|
|
|
8
8
|
type Status = "unknown" | "initialiazing" | "running" | "stopping" | "destroying";
|
|
9
9
|
|
|
10
|
-
const TICK_START = 42;
|
|
10
|
+
const TICK_START = 42; // RANDOM ? ;-) value to avoid zero division
|
|
11
11
|
|
|
12
12
|
import { Options, Statistics, ErrorsList } from "./interfaces";
|
|
13
13
|
|
|
@@ -19,18 +19,21 @@ const defaultOptions: Options = {
|
|
|
19
19
|
|
|
20
20
|
class Engine {
|
|
21
21
|
status: Status = "unknown";
|
|
22
|
-
readonly
|
|
23
|
-
|
|
22
|
+
readonly options: Options;
|
|
23
|
+
tags: { [key: string]: string | number | boolean } = {};
|
|
24
|
+
readonly runners: cqRunner.Runner[] = [];
|
|
25
|
+
readonly queues: cqQueue.Queue[] = [];
|
|
24
26
|
|
|
25
27
|
constructor(opt: Options) {
|
|
26
28
|
this.status = "initialiazing";
|
|
27
|
-
|
|
28
|
-
this
|
|
29
|
+
this.options = { ...defaultOptions, ...opt };
|
|
30
|
+
if (!this.options.id) this.options.id = _.Id.generate();
|
|
31
|
+
this.#checkOptionsConsistancy(this.options);
|
|
29
32
|
|
|
30
33
|
// create runners
|
|
31
|
-
for (let index = 0; index < (options.bestEffortRunners ?? 0); index++) this.createRunner({ priority:
|
|
32
|
-
if (options.logFunction) options.logFunction({ body: "engine has been initialized" });
|
|
33
|
-
if (options.autostart) this.start();
|
|
34
|
+
for (let index = 0; index < (this.options.bestEffortRunners ?? 0); index++) this.createRunner({ priority: cqRunner.Priorities.BestEffort });
|
|
35
|
+
if (this.options.logFunction) this.options.logFunction({ body: "engine has been initialized" });
|
|
36
|
+
if (this.options.autostart) this.start();
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
start(): void {
|
|
@@ -43,21 +46,21 @@ class Engine {
|
|
|
43
46
|
for (const runner of this.runners) runner.stop();
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
createQueue(options:
|
|
47
|
-
const queue = new
|
|
49
|
+
createQueue(options: cqQueue.Options = { priority: cqQueue.Priorities.Standard, weight: cqQueue.Weights.Default }): cqQueue.Queue {
|
|
50
|
+
const queue = new cqQueue.Queue({ logFunction: options.logFunction, ...options });
|
|
48
51
|
this.queues.push(queue);
|
|
49
52
|
this.#resetLastTick(options.priority);
|
|
50
53
|
return queue;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
createRunner(options:
|
|
54
|
-
const runner = new
|
|
56
|
+
createRunner(options: cqRunner.Options): cqRunner.Runner {
|
|
57
|
+
const runner = new cqRunner.Runner({ logFunction: options.logFunction, ...options }, this);
|
|
55
58
|
this.runners.push(runner);
|
|
56
59
|
return runner;
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
createTask(function_:
|
|
60
|
-
return new
|
|
62
|
+
createTask(function_: cqTask.FunctionToExecute, options: cqTask.Options): cqTask.Task {
|
|
63
|
+
return new cqTask.Task(function_, { logFunction: options.logFunction, ...options });
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
#checkOptionsConsistancy(options: Options): void {
|
|
@@ -65,19 +68,25 @@ class Engine {
|
|
|
65
68
|
if (!options.bestEffortRunners || options.bestEffortRunners < 1) throw new _.Errors.CQError(ErrorsList.bestEffortRunnersOptionMustBeGreaterThanZero);
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
#getSumWeightFunction:
|
|
71
|
+
#getSumWeightFunction: cqQueue.GetSumWeightFunction = (priority: number): number => {
|
|
69
72
|
let result = 0;
|
|
70
73
|
for (const q of this.queues) if (q.options.priority === priority) result += q.options.weight;
|
|
71
74
|
return result;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
#resetLastTick(priority: number): void {
|
|
75
|
-
for (
|
|
78
|
+
for (let i = 0; i < this.queues.length; i++) {
|
|
79
|
+
const q = this.queues[i];
|
|
80
|
+
if (q.options.priority === priority)
|
|
81
|
+
q.tick = TICK_START
|
|
82
|
+
+ this.#getSumWeightFunction(priority) / q.options.weight // set tick to max weight / queue weight to favorize low weight queues
|
|
83
|
+
+ i / 255; // slight increment to favorize first created queues
|
|
84
|
+
}
|
|
76
85
|
}
|
|
77
86
|
|
|
78
|
-
#getNextWeightedQueue(priority: number):
|
|
87
|
+
#getNextWeightedQueue(priority: number): cqQueue.Queue | undefined {
|
|
79
88
|
let minTick = Infinity;
|
|
80
|
-
let result:
|
|
89
|
+
let result: cqQueue.Queue | undefined = undefined;
|
|
81
90
|
for (const q of this.queues)
|
|
82
91
|
if (q.options.priority === priority && q.tick <= minTick) {
|
|
83
92
|
minTick = q.tick;
|
|
@@ -91,32 +100,12 @@ class Engine {
|
|
|
91
100
|
for (const q of this.queues) if (q.options.priority === priority) result += q.tasks.length;
|
|
92
101
|
return result;
|
|
93
102
|
}
|
|
94
|
-
/*
|
|
95
|
-
async getNextTask(priorityGreaterOrEqualTo: number): Promise<Task.Task> {
|
|
96
|
-
for (let priority = Queue.Priorities.Absolute; priority <= priorityGreaterOrEqualTo; priority++) {
|
|
97
|
-
// Iterate on Each PriorityLevel From Absolute to param function
|
|
98
|
-
while (this.#howMuchTasksWaitingInQueues(priority) > 0) {
|
|
99
|
-
let task: Task.Task | undefined;
|
|
100
|
-
while (!task) {
|
|
101
|
-
const nextPriorityQueue = this.#getNextQueue(priority); // Find Next Priority Queue to process
|
|
102
|
-
if (nextPriorityQueue) {
|
|
103
|
-
nextPriorityQueue.tick += this.#getSumWeightFunction(priority) / nextPriorityQueue.options.weight; // Update tick with related weight
|
|
104
|
-
if (nextPriorityQueue.tasks.length > 0) {
|
|
105
|
-
task = nextPriorityQueue.tasks.shift(); // If queue is not empty, this is our next Task to process
|
|
106
|
-
if (task) task.runner();
|
|
107
|
-
console.log(nextPriorityQueue.options.name, nextPriorityQueue.tick);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
*/
|
|
115
103
|
|
|
116
|
-
async getNextTask(priorityGreaterOrEqualTo: number): Promise<
|
|
117
|
-
|
|
104
|
+
async getNextTask(priorityGreaterOrEqualTo: number): Promise<cqTask.Task | undefined> {
|
|
105
|
+
if (this.status !== "running") return undefined;
|
|
106
|
+
let task: cqTask.Task | undefined = undefined;
|
|
118
107
|
// Iterate on Each PriorityLevel From Absolute to parameter
|
|
119
|
-
for (let priority =
|
|
108
|
+
for (let priority = cqQueue.Priorities.Absolute; priority >= priorityGreaterOrEqualTo; priority--) {
|
|
120
109
|
// Check if there is task waiting of this priority level
|
|
121
110
|
while (this.#howMuchTasksWaitingInQueues(priority) > 0) {
|
|
122
111
|
// Find Next Weighted Queue of this priority level to check
|
|
@@ -138,8 +127,8 @@ class Engine {
|
|
|
138
127
|
|
|
139
128
|
statistics(): Statistics {
|
|
140
129
|
let taskCount = 0;
|
|
141
|
-
const runners:
|
|
142
|
-
const queues:
|
|
130
|
+
const runners: cqRunner.Statistics[] = [];
|
|
131
|
+
const queues: cqQueue.Statistics[] = [];
|
|
143
132
|
for (const runner of this.runners) {
|
|
144
133
|
if (runner.status == "running") taskCount += 1;
|
|
145
134
|
runners.push(runner.statistics());
|
|
@@ -162,5 +151,5 @@ class Engine {
|
|
|
162
151
|
}
|
|
163
152
|
}
|
|
164
153
|
|
|
165
|
-
export { Options, Statistics, ErrorsList } from "./interfaces";
|
|
154
|
+
export { Options, Statistics, ErrorsList, LogFunction } from "./interfaces";
|
|
166
155
|
export { Engine };
|
package/src/engine/interfaces.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as _ from "../helpers";
|
|
2
2
|
|
|
3
|
-
import * as Runner from "../
|
|
4
|
-
import * as Queue from "../
|
|
3
|
+
import * as Runner from "../runners/interfaces";
|
|
4
|
+
import * as Queue from "../runners/interfaces";
|
|
5
5
|
|
|
6
6
|
interface Options {
|
|
7
|
+
id?: string;
|
|
7
8
|
autostart?: boolean;
|
|
8
9
|
bestEffortRunners?: number;
|
|
9
10
|
logFunction?: _.Logs.LogFunction;
|
package/src/helpers/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// clever-queue
|
|
2
|
-
import * as
|
|
2
|
+
import * as engine from "./engine";
|
|
3
3
|
|
|
4
|
-
function createEngine(options:
|
|
5
|
-
return new
|
|
4
|
+
function createEngine(options: engine.Options = {}): engine.Engine {
|
|
5
|
+
return new engine.Engine(options);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export * as
|
|
9
|
-
export * as
|
|
10
|
-
export * as
|
|
11
|
-
export * as
|
|
8
|
+
export * as engine from "./engine";
|
|
9
|
+
export * as runners from "./runners";
|
|
10
|
+
export * as queues from "./queues";
|
|
11
|
+
export * as tasks from "./tasks";
|
|
12
12
|
|
|
13
13
|
export { createEngine };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// clever-queue
|
|
2
2
|
|
|
3
3
|
import * as _ from "../helpers";
|
|
4
|
-
import * as
|
|
4
|
+
import * as cqTask from "../tasks";
|
|
5
5
|
import { Options, Statistics, ErrorsList, Priorities } from "./interfaces";
|
|
6
6
|
|
|
7
7
|
type GetSumWeightFunction = (priority: number) => number;
|
|
@@ -18,12 +18,15 @@ const defaultOptions: Options = {
|
|
|
18
18
|
class Queue {
|
|
19
19
|
status: Status = "unknown";
|
|
20
20
|
readonly options: Options;
|
|
21
|
+
tags: { [key: string]: string | number | boolean } = {};
|
|
22
|
+
|
|
21
23
|
tick: number = 0;
|
|
22
|
-
tasks:
|
|
24
|
+
tasks: cqTask.Task[] = [];
|
|
23
25
|
|
|
24
26
|
constructor(opt: Options) {
|
|
25
27
|
this.status = "initialiazing";
|
|
26
28
|
this.options = this.#checkOptionsConsistancy({ ...defaultOptions, ...opt });
|
|
29
|
+
if (!this.options.id) this.options.id = _.Id.generate();
|
|
27
30
|
if (this.options.autostart) this.start();
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -43,34 +46,14 @@ class Queue {
|
|
|
43
46
|
this.status = "stopping";
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (typeof function_ !== "function") {
|
|
49
|
-
console.error("raisong Task.ErrorsList.FunctionIsNotAFunction", Task.ErrorsList.FunctionIsNotAFunction);
|
|
50
|
-
throw new _.Errors.CQError(Task.ErrorsList.FunctionIsNotAFunction);
|
|
51
|
-
}
|
|
52
|
-
return new Promise((resolve, reject) => {
|
|
53
|
-
try {
|
|
54
|
-
const taskRunner = async (): Promise<void> => {
|
|
55
|
-
console.log("running Task");
|
|
56
|
-
const result = await function_();
|
|
57
|
-
console.log("task runned", result);
|
|
58
|
-
resolve(result);
|
|
59
|
-
};
|
|
60
|
-
const task = new Task.Task(taskRunner, {});
|
|
61
|
-
this.tasks.push(task);
|
|
62
|
-
} catch (error) {
|
|
63
|
-
reject(error);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
*/
|
|
68
|
-
async createTaskAndEnqueue(function_: Task.FunctionToExecute, options: Task.Options): Promise<unknown> {
|
|
69
|
-
const task = new Task.Task(function_, options);
|
|
49
|
+
async createTaskAndEnqueue(function_: cqTask.FunctionToExecute, options: cqTask.Options = {}): Promise<unknown> {
|
|
50
|
+
const task = new cqTask.Task(function_, options);
|
|
70
51
|
return this.enqueue(task);
|
|
71
52
|
}
|
|
72
53
|
|
|
73
|
-
async enqueue(task:
|
|
54
|
+
async enqueue(task: cqTask.Task): Promise<unknown> {
|
|
55
|
+
task.status = "queued";
|
|
56
|
+
task.queueId = this.options.id; // store queue that handle task for future references
|
|
74
57
|
return new Promise((resolve, reject) => {
|
|
75
58
|
task.resolver = resolve;
|
|
76
59
|
task.rejecter = reject;
|
|
@@ -89,5 +72,5 @@ class Queue {
|
|
|
89
72
|
}
|
|
90
73
|
}
|
|
91
74
|
|
|
92
|
-
export { Options, Statistics, ErrorsList, Priorities } from "./interfaces";
|
|
75
|
+
export { Options, Statistics, ErrorsList, Priorities, Weights } from "./interfaces";
|
|
93
76
|
export { Queue, GetSumWeightFunction };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as _ from "../helpers";
|
|
2
2
|
|
|
3
|
-
const Priorities = {
|
|
3
|
+
const Priorities = { BestEffort: 0, Standard: 128, Absolute: 255};
|
|
4
|
+
const Weights = { Lowest: 0, Default: 128, Highest: 255 };
|
|
4
5
|
|
|
5
6
|
interface Options {
|
|
6
|
-
|
|
7
|
+
id?: string;
|
|
7
8
|
priority: number;
|
|
8
9
|
weight: number;
|
|
9
10
|
autostart?: boolean;
|
|
@@ -35,4 +36,4 @@ const ErrorsList: {
|
|
|
35
36
|
NoOptionsOnQueueInitialization: { name: "NoOptionsOnQueueInitialization", message: "You must provide an options parameter to Queue" },
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
export { Options, Statistics, ErrorsList, Priorities };
|
|
39
|
+
export { Options, Statistics, ErrorsList, Priorities, Weights };
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import * as _ from "../helpers";
|
|
4
4
|
import { Options, Statistics, ErrorsList, Priorities } from "./interfaces";
|
|
5
|
-
import
|
|
5
|
+
import * as cqEngine from "../engine";
|
|
6
6
|
|
|
7
7
|
export * from "./interfaces";
|
|
8
8
|
|
|
9
|
-
type Status = "unknown" | "initialiazing" | "running" | "stopping" | "destroying";
|
|
9
|
+
type Status = "unknown" | "initialiazing" | "idle" | "running" | "stopping" | "destroying";
|
|
10
10
|
|
|
11
11
|
const defaultOptions: Options = {
|
|
12
12
|
priority: Priorities.BestEffort,
|
|
@@ -16,14 +16,16 @@ const defaultOptions: Options = {
|
|
|
16
16
|
|
|
17
17
|
class Runner {
|
|
18
18
|
status: Status = "unknown";
|
|
19
|
-
readonly
|
|
20
|
-
|
|
19
|
+
readonly options: Options;
|
|
20
|
+
tags: { [key: string]: string | number | boolean } = {};
|
|
21
|
+
readonly #engine: cqEngine.Engine;
|
|
21
22
|
|
|
22
|
-
constructor(opt: Options, engine: Engine) {
|
|
23
|
+
constructor(opt: Options, engine: cqEngine.Engine) {
|
|
23
24
|
this.status = "initialiazing";
|
|
24
|
-
this
|
|
25
|
+
this.options = this.#checkOptionsConsistancy({ ...defaultOptions, ...opt });
|
|
26
|
+
if (!this.options.id) this.options.id = _.Id.generate();
|
|
25
27
|
this.#engine = engine; // Store parent engine for later callbacks
|
|
26
|
-
if (this
|
|
28
|
+
if (this.options.autostart) this.start();
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
#checkOptionsConsistancy(options: Options): Options {
|
|
@@ -32,18 +34,21 @@ class Runner {
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
async #run(): Promise<void> {
|
|
35
|
-
while (
|
|
37
|
+
while (["idle", "running"].includes(this.status)) {
|
|
36
38
|
await new Promise((resolve) => setTimeout(resolve, 10)); // Do not freeze the event loop with infinite loop.
|
|
37
39
|
// get the next task to run according to this runner priority
|
|
38
|
-
const task = await this.#engine.getNextTask(this
|
|
40
|
+
const task = await this.#engine.getNextTask(this.options.priority);
|
|
39
41
|
if (task) {
|
|
40
|
-
|
|
42
|
+
if (this.status === "idle") this.status = "running";
|
|
43
|
+
task.runnerId = this.options.id; // store runner that handle task for future references
|
|
44
|
+
await task.run();
|
|
45
|
+
if (this.status === "running") this.status = "idle";
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
start(): void {
|
|
46
|
-
this.status = "
|
|
51
|
+
this.status = "idle";
|
|
47
52
|
this.#run();
|
|
48
53
|
}
|
|
49
54
|
|
|
@@ -54,7 +59,7 @@ class Runner {
|
|
|
54
59
|
statistics(): Statistics {
|
|
55
60
|
const stat: Statistics = {
|
|
56
61
|
status: this.status,
|
|
57
|
-
priority: this
|
|
62
|
+
priority: this.options.priority,
|
|
58
63
|
};
|
|
59
64
|
return stat;
|
|
60
65
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as _ from "../helpers";
|
|
2
2
|
|
|
3
3
|
interface Options {
|
|
4
|
+
id?: string;
|
|
4
5
|
autostart?: boolean;
|
|
5
6
|
priority: number;
|
|
6
7
|
logFunction?: _.Logs.LogFunction;
|
|
@@ -20,5 +21,5 @@ const ErrorsList: {
|
|
|
20
21
|
},
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
export { Priorities } from "../
|
|
24
|
+
export { Priorities } from "../queues/interfaces";
|
|
24
25
|
export { Options, Statistics, ErrorsList };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as _ from "../helpers";
|
|
4
4
|
import { Options, Statistics, ErrorsList, FunctionToExecute } from "./interfaces";
|
|
5
5
|
|
|
6
|
-
type Status = "unknown" | "initialiazing" | "running" | "
|
|
6
|
+
type Status = "unknown" | "initialiazing" | "queued" | "running" | "runned" | "exception" | "destroying";
|
|
7
7
|
|
|
8
8
|
const defaultOptions: Options = {
|
|
9
9
|
logFunction: undefined,
|
|
@@ -11,10 +11,14 @@ const defaultOptions: Options = {
|
|
|
11
11
|
|
|
12
12
|
class Task {
|
|
13
13
|
status: Status = "unknown";
|
|
14
|
-
readonly
|
|
14
|
+
readonly options: Options;
|
|
15
|
+
tags: { [key: string]: string | number | boolean } = {};
|
|
16
|
+
queueId: string | undefined;
|
|
17
|
+
runnerId: string | undefined;
|
|
18
|
+
|
|
15
19
|
#functionToExecute: FunctionToExecute;
|
|
16
20
|
result: unknown | undefined = undefined;
|
|
17
|
-
readonly
|
|
21
|
+
readonly run: () => Promise<unknown>;
|
|
18
22
|
|
|
19
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
24
|
resolver: (value: any | PromiseLike<any>) => void;
|
|
@@ -30,21 +34,24 @@ class Task {
|
|
|
30
34
|
|
|
31
35
|
constructor(function_: FunctionToExecute, opt: Options) {
|
|
32
36
|
this.status = "initialiazing";
|
|
33
|
-
this
|
|
34
|
-
|
|
37
|
+
this.options = this.#checkOptionsConsistancy({ ...defaultOptions, ...opt });
|
|
38
|
+
if (!this.options.id) this.options.id = _.Id.generate();
|
|
35
39
|
if (typeof function_ !== "function") throw new _.Errors.CQError(ErrorsList.FunctionIsNotAFunction);
|
|
36
40
|
|
|
37
41
|
this.resolver = this.#defaultResolver;
|
|
38
42
|
this.rejecter = this.#defaultRejecter;
|
|
39
43
|
this.#functionToExecute = function_;
|
|
40
44
|
|
|
41
|
-
this.
|
|
45
|
+
this.run = async (): Promise<void> => {
|
|
42
46
|
this.status = "running";
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
try {
|
|
48
|
+
this.result = await this.#functionToExecute();
|
|
49
|
+
this.status = "runned";
|
|
50
|
+
this.resolver(this.result);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
this.status = "exception";
|
|
53
|
+
this.rejecter(new _.Errors.CQError(ErrorsList.FunctionRaisedAnHundledException));
|
|
54
|
+
}
|
|
48
55
|
};
|
|
49
56
|
}
|
|
50
57
|
|
|
@@ -4,6 +4,7 @@ import * as _ from "../helpers";
|
|
|
4
4
|
type FunctionToExecute = (...arguments_: any[]) => Promise<any> | void;
|
|
5
5
|
|
|
6
6
|
interface Options {
|
|
7
|
+
id?: string;
|
|
7
8
|
timeout?: number;
|
|
8
9
|
logFunction?: _.Logs.LogFunction;
|
|
9
10
|
}
|
|
@@ -12,7 +13,7 @@ interface Statistics {
|
|
|
12
13
|
status: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
type ErrorListKeys = "NoOptionsOnTaskInitialization" | "FunctionIsNotAFunction" | "TaskRunnerNotInitialized";
|
|
16
|
+
type ErrorListKeys = "NoOptionsOnTaskInitialization" | "FunctionIsNotAFunction" | "FunctionRaisedAnHundledException" | "TaskRunnerNotInitialized";
|
|
16
17
|
|
|
17
18
|
const ErrorsList: {
|
|
18
19
|
[index in ErrorListKeys]: { name: string; message: string };
|
|
@@ -25,6 +26,10 @@ const ErrorsList: {
|
|
|
25
26
|
name: "FunctionIsNotAFunction",
|
|
26
27
|
message: "FunctionIsNotAFunction",
|
|
27
28
|
},
|
|
29
|
+
FunctionRaisedAnHundledException: {
|
|
30
|
+
name: "FunctionRaisedAnHundledException",
|
|
31
|
+
message: "FunctionRaisedAnHundledException",
|
|
32
|
+
},
|
|
28
33
|
TaskRunnerNotInitialized: {
|
|
29
34
|
name: "TaskRunnerNotInitialized",
|
|
30
35
|
message: "TaskRunnerNotInitialized",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
|
|
4
|
+
import * as cleverQueue from "../../dist/index.js";
|
|
5
|
+
|
|
6
|
+
const engineOptions = {
|
|
7
|
+
bestEffortRunners: 1,
|
|
8
|
+
logFunction: undefined,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const task = async (string_) => {
|
|
12
|
+
// console.log(string_, "Start", new Date());
|
|
13
|
+
await new Promise((resolve, reject) => setTimeout(reject(string_), 2000));
|
|
14
|
+
// console.log(string_, "End", new Date());
|
|
15
|
+
return string_;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe("Issue #1", () => {
|
|
19
|
+
it("Exception if function passed raised an exception", () => {
|
|
20
|
+
const engine = cleverQueue.createEngine(engineOptions);
|
|
21
|
+
const queue1 = engine.createQueue({
|
|
22
|
+
name: "A",
|
|
23
|
+
priority: cleverQueue.queues.Priorities.Absolute,
|
|
24
|
+
weight: 50,
|
|
25
|
+
});
|
|
26
|
+
assert.rejects(
|
|
27
|
+
async () => {
|
|
28
|
+
const myTask = engine.createTask(() => task("taskMustFail"), {});
|
|
29
|
+
await queue1.enqueue(myTask);
|
|
30
|
+
},
|
|
31
|
+
{ name: "FunctionRaisedAnHundledException" },
|
|
32
|
+
);
|
|
33
|
+
engine.stop();
|
|
34
|
+
});
|
|
35
|
+
});
|