async-queue-runner 1.0.0 → 1.0.2
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 +171 -0
- package/package.json +14 -3
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# async-queue-runner
|
|
2
|
+
|
|
3
|
+
Library to run extendable async queues with branching, context mutation, and optional locking scopes.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install async-queue-runner
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
yarn add async-queue-runner
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Action, QueueRunner, QueueContext } from 'async-queue-runner';
|
|
19
|
+
|
|
20
|
+
type Ctx = { value: number };
|
|
21
|
+
|
|
22
|
+
class Increment extends Action<Ctx> {
|
|
23
|
+
async execute({ value, extend }: Ctx & QueueContext): Promise<void> {
|
|
24
|
+
extend({ value: value + 1 });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class StopIfTooHigh extends Action<Ctx> {
|
|
29
|
+
async execute({ value, abort }: Ctx & QueueContext): Promise<void> {
|
|
30
|
+
if (value >= 3) abort();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const runner = new QueueRunner();
|
|
35
|
+
runner.add([Increment, Increment, StopIfTooHigh, Increment], { value: 0 });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Core concepts
|
|
39
|
+
|
|
40
|
+
### QueueAction
|
|
41
|
+
|
|
42
|
+
Queue items can be either:
|
|
43
|
+
- `IAction` instances (created actions), or
|
|
44
|
+
- `Action` classes (constructors).
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
runner.add([
|
|
48
|
+
new Increment(),
|
|
49
|
+
Increment,
|
|
50
|
+
]);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Action classes are instantiated with no arguments. If you need constructor
|
|
54
|
+
options, pass an instance instead of a class.
|
|
55
|
+
|
|
56
|
+
### QueueContext
|
|
57
|
+
|
|
58
|
+
Each action receives a context that can be extended at runtime.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
type QueueContext = {
|
|
62
|
+
push(actions: QueueAction[]): void
|
|
63
|
+
extend(obj: object): void
|
|
64
|
+
name(): string
|
|
65
|
+
abort(): void
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- `push` inserts new actions at the front of the remaining queue.
|
|
70
|
+
- `extend` merges new fields into the context.
|
|
71
|
+
- `name` returns the queue name.
|
|
72
|
+
- `abort` clears the remaining queue (preventive stop).
|
|
73
|
+
|
|
74
|
+
### Error handling per action
|
|
75
|
+
|
|
76
|
+
Every action has `onError(error, context)`. The default implementation:
|
|
77
|
+
- logs to `context.logger.error(error)` when present, and
|
|
78
|
+
- calls `context.abort()` to stop the queue.
|
|
79
|
+
|
|
80
|
+
Override it to implement recovery:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
class Recoverable extends Action<{ recovered?: boolean }> {
|
|
84
|
+
async execute(): Promise<void> {
|
|
85
|
+
throw new Error('boom');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async onError(_error: Error, context: QueueContext): Promise<void> {
|
|
89
|
+
context.extend({ recovered: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Locking
|
|
95
|
+
|
|
96
|
+
Locking ensures that actions with the same scope do not run at the same time across queues.
|
|
97
|
+
|
|
98
|
+
### Define a locking action
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { lockingClassFactory } from 'async-queue-runner';
|
|
102
|
+
|
|
103
|
+
class WithBrowserLock extends lockingClassFactory<{ url: string }>('browser') {
|
|
104
|
+
async execute({ url }: { url: string } & QueueContext): Promise<void> {
|
|
105
|
+
// protected by lock scope "browser"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Provide a lock manager
|
|
111
|
+
|
|
112
|
+
`QueueRunner` provides a shared lock manager automatically.
|
|
113
|
+
For direct `AsyncQueue` use, pass a `LockManager` instance.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { AsyncQueue, LockManager } from 'async-queue-runner';
|
|
117
|
+
|
|
118
|
+
const queue = new AsyncQueue({
|
|
119
|
+
name: 'q1',
|
|
120
|
+
actions: [WithBrowserLock],
|
|
121
|
+
lockingContext: new LockManager(),
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Lock scopes must be non-empty strings. Invalid scopes throw early.
|
|
126
|
+
|
|
127
|
+
## Utilities
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { util } from 'async-queue-runner';
|
|
131
|
+
|
|
132
|
+
// fixed delay
|
|
133
|
+
util.delay(500);
|
|
134
|
+
|
|
135
|
+
// branching
|
|
136
|
+
util.if<{ flag: boolean }>(
|
|
137
|
+
({ flag }) => flag,
|
|
138
|
+
{ then: [SomeAction], else: [OtherAction] }
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// conditional actions
|
|
142
|
+
util.valid<{ count: number }>(
|
|
143
|
+
({ count }) => count > 0,
|
|
144
|
+
[SomeAction]
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// immediate abort action
|
|
148
|
+
util.abort;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## QueueRunner vs AsyncQueue
|
|
152
|
+
|
|
153
|
+
- `QueueRunner` manages multiple queues and shared locking.
|
|
154
|
+
- `AsyncQueue` runs a single queue directly.
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { QueueRunner } from 'async-queue-runner';
|
|
158
|
+
|
|
159
|
+
const runner = new QueueRunner();
|
|
160
|
+
runner.add([SomeAction], { initial: true }, 'my-queue');
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Logging
|
|
164
|
+
|
|
165
|
+
`AsyncQueue` accepts a logger for queue-level logs.
|
|
166
|
+
Default `onError` uses `context.logger.error` if available in context.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
const runner = new QueueRunner({ logger: myQueueLogger });
|
|
170
|
+
runner.add([SomeAction], { logger: myActionLogger });
|
|
171
|
+
```
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "async-queue-runner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Library to run in parallel extendable queue of tasks",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"compile": "tsc --project ./tsconfig.json",
|
|
7
|
-
"copy:package:json": "copyfiles ./package.json ./built/ && copyfiles ./_esm/* ./built && copyfiles ./_cjs/* ./built",
|
|
7
|
+
"copy:package:json": "copyfiles ./package.json ./README.md ./built/ && copyfiles ./_esm/* ./built && copyfiles ./_cjs/* ./built",
|
|
8
8
|
"build:all": "tsc -p ./tsconfig.esm.json && tsc -p ./tsconfig.cjs.json && tsc -p ./tsconfig.types.json",
|
|
9
9
|
"build": "del-cli ./built/ && npm run build:all && npm run copy:package:json",
|
|
10
10
|
"bump": "npm version minor",
|
|
@@ -22,7 +22,18 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"types": "./_types/index.d.ts",
|
|
25
|
-
"keywords": [
|
|
25
|
+
"keywords": [
|
|
26
|
+
"async",
|
|
27
|
+
"queue",
|
|
28
|
+
"task-queue",
|
|
29
|
+
"runner",
|
|
30
|
+
"workflow",
|
|
31
|
+
"jobs",
|
|
32
|
+
"locking",
|
|
33
|
+
"actions",
|
|
34
|
+
"typescript",
|
|
35
|
+
"node"
|
|
36
|
+
],
|
|
26
37
|
"author": "Eugeny Dementev",
|
|
27
38
|
"license": "MIT",
|
|
28
39
|
"devDependencies": {
|