@push.rocks/taskbuffer 3.1.10 โ 3.4.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/LICENSE +1 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/index.d.ts +4 -1
- package/dist_ts/index.js +3 -1
- package/dist_ts/taskbuffer.classes.task.d.ts +43 -9
- package/dist_ts/taskbuffer.classes.task.js +105 -1
- package/dist_ts/taskbuffer.classes.taskmanager.d.ts +23 -6
- package/dist_ts/taskbuffer.classes.taskmanager.js +98 -1
- package/dist_ts/taskbuffer.classes.taskstep.d.ts +27 -0
- package/dist_ts/taskbuffer.classes.taskstep.js +37 -0
- package/dist_ts/taskbuffer.interfaces.d.ts +36 -0
- package/dist_ts/taskbuffer.interfaces.js +2 -0
- package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
- package/dist_ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/ts/index.d.ts +13 -0
- package/dist_ts_web/ts/index.js +12 -0
- package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.d.ts +8 -0
- package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.js +28 -0
- package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.d.ts +13 -0
- package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.js +31 -0
- package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.d.ts +27 -0
- package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.js +5 -0
- package/dist_ts_web/ts/taskbuffer.classes.task.d.ts +86 -0
- package/dist_ts_web/ts/taskbuffer.classes.task.js +257 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskchain.d.ts +14 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskchain.js +51 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.d.ts +10 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.js +20 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskmanager.d.ts +49 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskmanager.js +208 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskonce.d.ts +11 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskonce.js +20 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskparallel.d.ts +7 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskparallel.js +23 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskrunner.d.ts +30 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskrunner.js +54 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskstep.d.ts +27 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskstep.js +37 -0
- package/dist_ts_web/ts/taskbuffer.interfaces.d.ts +36 -0
- package/dist_ts_web/ts/taskbuffer.interfaces.js +2 -0
- package/dist_ts_web/ts/taskbuffer.logging.d.ts +2 -0
- package/dist_ts_web/ts/taskbuffer.logging.js +3 -0
- package/dist_ts_web/ts/taskbuffer.plugins.d.ts +8 -0
- package/dist_ts_web/ts/taskbuffer.plugins.js +9 -0
- package/dist_ts_web/ts_web/00_commitinfo_data.d.ts +8 -0
- package/dist_ts_web/ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/ts_web/demorunner.d.ts +1 -0
- package/dist_ts_web/ts_web/demorunner.js +33 -0
- package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.d.ts +2 -0
- package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.js +285 -0
- package/dist_ts_web/ts_web/index.d.ts +2 -0
- package/dist_ts_web/ts_web/index.js +3 -0
- package/dist_ts_web/ts_web/taskbuffer-dashboard.d.ts +24 -0
- package/dist_ts_web/ts_web/taskbuffer-dashboard.js +557 -0
- package/package.json +6 -5
- package/readme.md +421 -643
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +9 -1
- package/ts/taskbuffer.classes.task.ts +145 -18
- package/ts/taskbuffer.classes.taskmanager.ts +129 -9
- package/ts/taskbuffer.classes.taskstep.ts +57 -0
- package/ts/taskbuffer.interfaces.ts +39 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/elements/taskbuffer-dashboard.demo.ts +311 -0
- package/ts_web/index.ts +12 -0
- package/ts_web/taskbuffer-dashboard.ts +541 -0
package/readme.md
CHANGED
|
@@ -1,417 +1,121 @@
|
|
|
1
1
|
# @push.rocks/taskbuffer ๐
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Modern TypeScript task orchestration with smart buffering, scheduling, and real-time progress tracking**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@push.rocks/taskbuffer)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
npm install @push.rocks/taskbuffer --save
|
|
9
|
-
```
|
|
9
|
+
## ๐ Features
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- **๐ฏ Type-Safe Task Management** - Full TypeScript support with generics and type inference
|
|
12
|
+
- **๐ Real-Time Progress Tracking** - Step-based progress with percentage weights
|
|
13
|
+
- **โก Smart Buffering** - Intelligent request debouncing and batching
|
|
14
|
+
- **โฐ Cron Scheduling** - Schedule tasks with cron expressions
|
|
15
|
+
- **๐ Task Chains & Parallel Execution** - Sequential and parallel task orchestration
|
|
16
|
+
- **๐จ Web Component Dashboard** - Real-time visualization of task execution
|
|
17
|
+
- **๐ Comprehensive Metadata** - Track execution history, duration, and status
|
|
18
|
+
- **๐ Thread-Safe Operations** - Concurrency control and execution limits
|
|
19
|
+
- **๐ญ Event-Driven Architecture** - Observable task lifecycle events
|
|
20
|
+
|
|
21
|
+
## ๐ฆ Installation
|
|
12
22
|
|
|
13
23
|
```bash
|
|
24
|
+
npm install @push.rocks/taskbuffer
|
|
25
|
+
# or
|
|
14
26
|
pnpm add @push.rocks/taskbuffer
|
|
27
|
+
# or
|
|
28
|
+
yarn add @push.rocks/taskbuffer
|
|
15
29
|
```
|
|
16
30
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
In the modern JavaScript ecosystem, managing asynchronous tasks efficiently is crucial. Whether you're building a data pipeline, managing API rate limits, or orchestrating complex workflows, **@push.rocks/taskbuffer** provides the tools you need:
|
|
20
|
-
|
|
21
|
-
- **๐ฏ TypeScript-first**: Built with TypeScript for TypeScript - enjoy complete type safety and excellent IDE support
|
|
22
|
-
- **โก Flexible execution**: From simple tasks to complex parallel workflows with dependencies
|
|
23
|
-
- **๐ Smart buffering**: Control concurrent executions with intelligent buffer management
|
|
24
|
-
- **โฐ Built-in scheduling**: Cron-based task scheduling without additional dependencies
|
|
25
|
-
- **๐ญ Multiple paradigms**: Support for debounced, throttled, and one-time execution patterns
|
|
26
|
-
- **๐ Extensible**: Clean architecture that's easy to extend and customize
|
|
27
|
-
- **๐ Zero dependencies on external schedulers**: Everything you need is included
|
|
28
|
-
|
|
29
|
-
## Core Concepts ๐
|
|
30
|
-
|
|
31
|
-
### Task
|
|
32
|
-
|
|
33
|
-
The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control.
|
|
34
|
-
|
|
35
|
-
### Taskchain
|
|
36
|
-
|
|
37
|
-
Sequential task execution - tasks run one after another, with results passed along the chain.
|
|
38
|
-
|
|
39
|
-
### Taskparallel
|
|
40
|
-
|
|
41
|
-
Parallel task execution - multiple tasks run simultaneously for maximum performance.
|
|
42
|
-
|
|
43
|
-
### TaskManager
|
|
44
|
-
|
|
45
|
-
Centralized task scheduling and management using cron expressions.
|
|
46
|
-
|
|
47
|
-
### TaskDebounced
|
|
31
|
+
## ๐ Quick Start
|
|
48
32
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
### TaskOnce
|
|
52
|
-
|
|
53
|
-
Singleton task execution - ensures a task runs exactly once, perfect for initialization routines.
|
|
54
|
-
|
|
55
|
-
## Quick Start ๐
|
|
56
|
-
|
|
57
|
-
### Basic Task Execution
|
|
33
|
+
### Basic Task Creation
|
|
58
34
|
|
|
59
35
|
```typescript
|
|
60
|
-
import { Task } from '@push.rocks/taskbuffer';
|
|
36
|
+
import { Task, TaskManager } from '@push.rocks/taskbuffer';
|
|
61
37
|
|
|
62
38
|
// Create a simple task
|
|
63
|
-
const
|
|
64
|
-
name: '
|
|
65
|
-
taskFunction: async () => {
|
|
66
|
-
const data = await fetchData();
|
|
67
|
-
return processData(data);
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Execute the task
|
|
72
|
-
const result = await myTask.trigger();
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## TypeScript Generics Support ๐ฌ
|
|
76
|
-
|
|
77
|
-
TaskBuffer leverages TypeScript's powerful generics system for complete type safety across your task chains and workflows.
|
|
78
|
-
|
|
79
|
-
### Generic Task Functions
|
|
80
|
-
|
|
81
|
-
Tasks support generic type parameters for both input and output types:
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
import { Task, ITaskFunction } from '@push.rocks/taskbuffer';
|
|
85
|
-
|
|
86
|
-
// Define typed interfaces
|
|
87
|
-
interface UserData {
|
|
88
|
-
id: string;
|
|
89
|
-
name: string;
|
|
90
|
-
email: string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
interface ProcessedUser {
|
|
94
|
-
userId: string;
|
|
95
|
-
displayName: string;
|
|
96
|
-
normalized: boolean;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Create strongly typed tasks
|
|
100
|
-
const processUserTask = new Task<UserData, ProcessedUser>({
|
|
101
|
-
name: 'ProcessUser',
|
|
102
|
-
taskFunction: async (user: UserData): Promise<ProcessedUser> => {
|
|
103
|
-
return {
|
|
104
|
-
userId: user.id,
|
|
105
|
-
displayName: user.name.toUpperCase(),
|
|
106
|
-
normalized: true
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Type safety enforced at compile time
|
|
112
|
-
const result: ProcessedUser = await processUserTask.trigger({
|
|
113
|
-
id: '123',
|
|
114
|
-
name: 'John Doe',
|
|
115
|
-
email: 'john@example.com'
|
|
116
|
-
});
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Generic Setup Values
|
|
120
|
-
|
|
121
|
-
Tasks can accept setup values through generics, perfect for configuration:
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
interface TaskConfig {
|
|
125
|
-
apiEndpoint: string;
|
|
126
|
-
retryCount: number;
|
|
127
|
-
timeout: number;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const configuredTask = new Task<TaskConfig>({
|
|
131
|
-
name: 'ConfiguredTask',
|
|
132
|
-
taskSetup: async () => ({
|
|
133
|
-
apiEndpoint: 'https://api.example.com',
|
|
134
|
-
retryCount: 3,
|
|
135
|
-
timeout: 5000
|
|
136
|
-
}),
|
|
137
|
-
taskFunction: async (data: any, setupValue: TaskConfig) => {
|
|
138
|
-
// setupValue is fully typed!
|
|
139
|
-
for (let i = 0; i < setupValue.retryCount; i++) {
|
|
140
|
-
try {
|
|
141
|
-
return await fetchWithTimeout(
|
|
142
|
-
setupValue.apiEndpoint,
|
|
143
|
-
setupValue.timeout
|
|
144
|
-
);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
if (i === setupValue.retryCount - 1) throw error;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### Type-Safe Task Chains
|
|
154
|
-
|
|
155
|
-
Chain tasks with preserved type flow:
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
// Each task knows its input and output types
|
|
159
|
-
const fetchTask = new Task<void, UserData[]>({
|
|
160
|
-
name: 'FetchUsers',
|
|
161
|
-
taskFunction: async (): Promise<UserData[]> => {
|
|
162
|
-
return await api.getUsers();
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const filterTask = new Task<UserData[], UserData[]>({
|
|
167
|
-
name: 'FilterActive',
|
|
168
|
-
taskFunction: async (users: UserData[]): Promise<UserData[]> => {
|
|
169
|
-
return users.filter(user => user.isActive);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const mapTask = new Task<UserData[], ProcessedUser[]>({
|
|
174
|
-
name: 'MapToProcessed',
|
|
175
|
-
taskFunction: async (users: UserData[]): Promise<ProcessedUser[]> => {
|
|
176
|
-
return users.map(transformUser);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Type safety flows through the chain
|
|
181
|
-
const chain = new Taskchain({
|
|
182
|
-
name: 'UserPipeline',
|
|
183
|
-
taskArray: [fetchTask, filterTask, mapTask]
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
const finalResult: ProcessedUser[] = await chain.trigger();
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## Buffer Behavior Deep Dive ๐
|
|
190
|
-
|
|
191
|
-
The buffer system in TaskBuffer provides intelligent control over concurrent executions, preventing system overload while maximizing throughput.
|
|
192
|
-
|
|
193
|
-
### How Buffering Works
|
|
194
|
-
|
|
195
|
-
When a task is buffered, TaskBuffer manages a queue of executions:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
const bufferedTask = new Task({
|
|
199
|
-
name: 'BufferedOperation',
|
|
39
|
+
const dataProcessor = new Task({
|
|
40
|
+
name: 'ProcessData',
|
|
200
41
|
taskFunction: async (data) => {
|
|
201
42
|
console.log(`Processing: ${data}`);
|
|
202
|
-
|
|
43
|
+
// Your async logic here
|
|
203
44
|
return `Processed: ${data}`;
|
|
204
|
-
}
|
|
205
|
-
buffered: true,
|
|
206
|
-
bufferMax: 3 // Maximum 3 concurrent executions
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// Trigger 10 executions rapidly
|
|
210
|
-
for (let i = 0; i < 10; i++) {
|
|
211
|
-
bufferedTask.trigger(`Item ${i}`);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// What happens:
|
|
215
|
-
// 1. First 3 tasks start immediately
|
|
216
|
-
// 2. Items 4-10 are queued
|
|
217
|
-
// 3. As each task completes, next queued item starts
|
|
218
|
-
// 4. Never more than 3 tasks running simultaneously
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Buffer Truncation Behavior
|
|
222
|
-
|
|
223
|
-
When buffer limit is reached, new calls are intelligently managed:
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
const truncatingTask = new Task({
|
|
227
|
-
name: 'TruncatingBuffer',
|
|
228
|
-
taskFunction: async (data) => {
|
|
229
|
-
await processData(data);
|
|
230
|
-
},
|
|
231
|
-
buffered: true,
|
|
232
|
-
bufferMax: 5 // Maximum 5 in buffer
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// Rapid fire 100 calls
|
|
236
|
-
for (let i = 0; i < 100; i++) {
|
|
237
|
-
truncatingTask.trigger(`Data ${i}`);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Buffer behavior:
|
|
241
|
-
// - First 5 calls: Added to buffer and start processing
|
|
242
|
-
// - Calls 6-100: Each overwrites the 5th buffer slot
|
|
243
|
-
// - Result: Only processes items 0,1,2,3, and 99 (last one)
|
|
244
|
-
// - This prevents memory overflow in high-frequency scenarios
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Advanced Buffer Strategies
|
|
248
|
-
|
|
249
|
-
#### 1. **Sliding Window Buffer**
|
|
250
|
-
Perfect for real-time data processing where only recent items matter:
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
const slidingWindowTask = new Task({
|
|
254
|
-
name: 'SlidingWindow',
|
|
255
|
-
taskFunction: async (data) => {
|
|
256
|
-
return await analyzeRecentData(data);
|
|
257
|
-
},
|
|
258
|
-
buffered: true,
|
|
259
|
-
bufferMax: 10, // Keep last 10 items
|
|
260
|
-
execDelay: 100 // Process every 100ms
|
|
45
|
+
}
|
|
261
46
|
});
|
|
262
47
|
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
// Older items automatically dropped when buffer full
|
|
267
|
-
});
|
|
48
|
+
// Execute the task
|
|
49
|
+
const result = await dataProcessor.trigger('my-data');
|
|
50
|
+
console.log(result); // "Processed: my-data"
|
|
268
51
|
```
|
|
269
52
|
|
|
270
|
-
|
|
271
|
-
Combine buffering with execution delays for rate limiting:
|
|
53
|
+
### Tasks with Progress Tracking ๐
|
|
272
54
|
|
|
273
55
|
```typescript
|
|
274
|
-
const
|
|
275
|
-
name: '
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
56
|
+
const deploymentTask = new Task({
|
|
57
|
+
name: 'DeployApplication',
|
|
58
|
+
steps: [
|
|
59
|
+
{ name: 'build', description: 'Building application', percentage: 30 },
|
|
60
|
+
{ name: 'test', description: 'Running tests', percentage: 20 },
|
|
61
|
+
{ name: 'deploy', description: 'Deploying to server', percentage: 40 },
|
|
62
|
+
{ name: 'verify', description: 'Verifying deployment', percentage: 10 }
|
|
63
|
+
] as const, // Use 'as const' for type inference
|
|
64
|
+
taskFunction: async function() {
|
|
65
|
+
// TypeScript knows these step names!
|
|
66
|
+
this.notifyStep('build');
|
|
67
|
+
await buildApplication();
|
|
68
|
+
|
|
69
|
+
this.notifyStep('test');
|
|
70
|
+
await runTests();
|
|
71
|
+
|
|
72
|
+
this.notifyStep('deploy');
|
|
73
|
+
await deployToServer();
|
|
74
|
+
|
|
75
|
+
this.notifyStep('verify');
|
|
76
|
+
await verifyDeployment();
|
|
77
|
+
|
|
78
|
+
return 'Deployment successful!';
|
|
79
|
+
}
|
|
282
80
|
});
|
|
283
81
|
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
#### 3. **Priority Buffer** (Custom Implementation)
|
|
289
|
-
Implement priority queuing with buffer management:
|
|
290
|
-
|
|
291
|
-
```typescript
|
|
292
|
-
class PriorityBufferedTask extends Task {
|
|
293
|
-
private priorityQueue: Array<{data: any, priority: number}> = [];
|
|
294
|
-
|
|
295
|
-
constructor(options) {
|
|
296
|
-
super({
|
|
297
|
-
...options,
|
|
298
|
-
taskFunction: async (item) => {
|
|
299
|
-
// Process based on priority
|
|
300
|
-
return await this.processByPriority(item);
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
triggerWithPriority(data: any, priority: number) {
|
|
306
|
-
if (this.priorityQueue.length >= this.bufferMax) {
|
|
307
|
-
// Remove lowest priority item if buffer full
|
|
308
|
-
this.priorityQueue.sort((a, b) => b.priority - a.priority);
|
|
309
|
-
this.priorityQueue.pop();
|
|
310
|
-
}
|
|
311
|
-
this.priorityQueue.push({data, priority});
|
|
312
|
-
this.priorityQueue.sort((a, b) => b.priority - a.priority);
|
|
313
|
-
return this.trigger(this.priorityQueue.shift());
|
|
314
|
-
}
|
|
315
|
-
}
|
|
82
|
+
// Monitor progress
|
|
83
|
+
console.log(deploymentTask.getProgress()); // 0-100
|
|
84
|
+
console.log(deploymentTask.getStepsMetadata()); // Detailed step info
|
|
316
85
|
```
|
|
317
86
|
|
|
318
|
-
|
|
87
|
+
## ๐ฏ Core Concepts
|
|
319
88
|
|
|
320
|
-
|
|
89
|
+
### Task Buffering - Intelligent Request Management
|
|
321
90
|
|
|
322
|
-
|
|
323
|
-
const monitoredTask = new Task({
|
|
324
|
-
name: 'MonitoredBuffer',
|
|
325
|
-
taskFunction: async (data) => {
|
|
326
|
-
const startTime = Date.now();
|
|
327
|
-
const result = await processData(data);
|
|
328
|
-
console.log(`Processing time: ${Date.now() - startTime}ms`);
|
|
329
|
-
console.log(`Buffer utilization: ${monitoredTask.bufferRunner.bufferCounter}/${monitoredTask.bufferMax}`);
|
|
330
|
-
return result;
|
|
331
|
-
},
|
|
332
|
-
buffered: true,
|
|
333
|
-
bufferMax: 20
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// Monitor buffer saturation
|
|
337
|
-
setInterval(() => {
|
|
338
|
-
const utilization = (monitoredTask.bufferRunner.bufferCounter / monitoredTask.bufferMax) * 100;
|
|
339
|
-
if (utilization > 80) {
|
|
340
|
-
console.warn(`Buffer near capacity: ${utilization.toFixed(1)}%`);
|
|
341
|
-
}
|
|
342
|
-
}, 1000);
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### Buffer Best Practices
|
|
346
|
-
|
|
347
|
-
1. **Choose appropriate buffer sizes**:
|
|
348
|
-
- I/O operations: 5-10 concurrent
|
|
349
|
-
- CPU-intensive: Number of cores
|
|
350
|
-
- API calls: Based on rate limits
|
|
351
|
-
|
|
352
|
-
2. **Handle buffer overflow gracefully**:
|
|
353
|
-
```typescript
|
|
354
|
-
const task = new Task({
|
|
355
|
-
taskFunction: async (data) => {
|
|
356
|
-
try {
|
|
357
|
-
return await process(data);
|
|
358
|
-
} catch (error) {
|
|
359
|
-
if (error.code === 'BUFFER_OVERFLOW') {
|
|
360
|
-
// Implement backoff strategy
|
|
361
|
-
await delay(1000);
|
|
362
|
-
return task.trigger(data);
|
|
363
|
-
}
|
|
364
|
-
throw error;
|
|
365
|
-
}
|
|
366
|
-
},
|
|
367
|
-
buffered: true,
|
|
368
|
-
bufferMax: 10
|
|
369
|
-
});
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
3. **Monitor and adjust dynamically**:
|
|
373
|
-
```typescript
|
|
374
|
-
// Adjust buffer size based on system load
|
|
375
|
-
const adaptiveTask = new Task({
|
|
376
|
-
name: 'AdaptiveBuffer',
|
|
377
|
-
taskFunction: async (data) => {
|
|
378
|
-
const cpuLoad = await getSystemLoad();
|
|
379
|
-
if (cpuLoad > 0.8) {
|
|
380
|
-
adaptiveTask.bufferMax = Math.max(2, adaptiveTask.bufferMax - 1);
|
|
381
|
-
} else if (cpuLoad < 0.5) {
|
|
382
|
-
adaptiveTask.bufferMax = Math.min(20, adaptiveTask.bufferMax + 1);
|
|
383
|
-
}
|
|
384
|
-
return await process(data);
|
|
385
|
-
},
|
|
386
|
-
buffered: true,
|
|
387
|
-
bufferMax: 10
|
|
388
|
-
});
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
### Buffered Execution (Rate Limiting)
|
|
392
|
-
|
|
393
|
-
Perfect for API calls or database operations that need throttling:
|
|
91
|
+
TaskBuffer's buffering system prevents overwhelming your system with rapid-fire requests:
|
|
394
92
|
|
|
395
93
|
```typescript
|
|
396
94
|
const apiTask = new Task({
|
|
397
|
-
name: '
|
|
398
|
-
taskFunction: async (endpoint
|
|
399
|
-
return await fetch(endpoint);
|
|
95
|
+
name: 'APIRequest',
|
|
96
|
+
taskFunction: async (endpoint) => {
|
|
97
|
+
return await fetch(endpoint).then(r => r.json());
|
|
400
98
|
},
|
|
401
99
|
buffered: true,
|
|
402
|
-
bufferMax:
|
|
403
|
-
execDelay:
|
|
100
|
+
bufferMax: 5, // Maximum 5 concurrent executions
|
|
101
|
+
execDelay: 100 // Minimum 100ms between executions
|
|
404
102
|
});
|
|
405
103
|
|
|
406
|
-
//
|
|
407
|
-
for (let i = 0; i <
|
|
104
|
+
// Rapid fire 100 calls - only 5 will execute concurrently
|
|
105
|
+
for (let i = 0; i < 100; i++) {
|
|
408
106
|
apiTask.trigger(`/api/data/${i}`);
|
|
409
107
|
}
|
|
410
108
|
```
|
|
411
109
|
|
|
110
|
+
**Buffer Behavior:**
|
|
111
|
+
- First `bufferMax` calls execute immediately
|
|
112
|
+
- Additional calls are queued
|
|
113
|
+
- When buffer is full, new calls overwrite the last queued item
|
|
114
|
+
- Perfect for real-time data streams where only recent data matters
|
|
115
|
+
|
|
412
116
|
### Task Chains - Sequential Workflows
|
|
413
117
|
|
|
414
|
-
Build complex workflows
|
|
118
|
+
Build complex workflows with automatic data flow:
|
|
415
119
|
|
|
416
120
|
```typescript
|
|
417
121
|
import { Task, Taskchain } from '@push.rocks/taskbuffer';
|
|
@@ -421,327 +125,345 @@ const fetchTask = new Task({
|
|
|
421
125
|
taskFunction: async () => {
|
|
422
126
|
const response = await fetch('/api/data');
|
|
423
127
|
return response.json();
|
|
424
|
-
}
|
|
128
|
+
}
|
|
425
129
|
});
|
|
426
130
|
|
|
427
131
|
const transformTask = new Task({
|
|
428
132
|
name: 'TransformData',
|
|
429
133
|
taskFunction: async (data) => {
|
|
430
|
-
return data.map(
|
|
134
|
+
return data.map(item => ({
|
|
431
135
|
...item,
|
|
432
|
-
|
|
433
|
-
timestamp: Date.now()
|
|
136
|
+
transformed: true,
|
|
137
|
+
timestamp: Date.now()
|
|
434
138
|
}));
|
|
435
|
-
}
|
|
139
|
+
}
|
|
436
140
|
});
|
|
437
141
|
|
|
438
142
|
const saveTask = new Task({
|
|
439
143
|
name: 'SaveData',
|
|
440
144
|
taskFunction: async (transformedData) => {
|
|
441
|
-
await database.
|
|
442
|
-
return
|
|
443
|
-
}
|
|
145
|
+
await database.save(transformedData);
|
|
146
|
+
return transformedData.length;
|
|
147
|
+
}
|
|
444
148
|
});
|
|
445
149
|
|
|
446
|
-
|
|
150
|
+
// Create and execute chain
|
|
151
|
+
const dataChain = new Taskchain({
|
|
447
152
|
name: 'DataPipeline',
|
|
448
|
-
|
|
153
|
+
tasks: [fetchTask, transformTask, saveTask]
|
|
449
154
|
});
|
|
450
155
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
console.log(`Processed ${result.saved} items`);
|
|
156
|
+
const savedCount = await dataChain.trigger();
|
|
157
|
+
console.log(`Saved ${savedCount} items`);
|
|
454
158
|
```
|
|
455
159
|
|
|
456
|
-
### Parallel Execution -
|
|
160
|
+
### Parallel Execution - Concurrent Processing
|
|
457
161
|
|
|
458
|
-
Execute multiple
|
|
162
|
+
Execute multiple tasks simultaneously:
|
|
459
163
|
|
|
460
164
|
```typescript
|
|
461
|
-
import {
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}),
|
|
472
|
-
);
|
|
473
|
-
|
|
474
|
-
const parallelFetch = new Taskparallel({
|
|
475
|
-
taskArray: tasks,
|
|
165
|
+
import { TaskParallel } from '@push.rocks/taskbuffer';
|
|
166
|
+
|
|
167
|
+
const parallel = new TaskParallel({
|
|
168
|
+
name: 'ParallelProcessor',
|
|
169
|
+
tasks: [
|
|
170
|
+
emailTask,
|
|
171
|
+
smsTask,
|
|
172
|
+
pushNotificationTask,
|
|
173
|
+
webhookTask
|
|
174
|
+
]
|
|
476
175
|
});
|
|
477
176
|
|
|
478
|
-
// All tasks execute
|
|
479
|
-
const
|
|
177
|
+
// All tasks execute concurrently
|
|
178
|
+
const results = await parallel.trigger(notificationData);
|
|
179
|
+
// results = [emailResult, smsResult, pushResult, webhookResult]
|
|
480
180
|
```
|
|
481
181
|
|
|
482
|
-
###
|
|
182
|
+
### TaskManager - Centralized Orchestration
|
|
483
183
|
|
|
484
|
-
|
|
184
|
+
Manage all your tasks from a single point:
|
|
485
185
|
|
|
486
186
|
```typescript
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
//
|
|
504
|
-
|
|
187
|
+
const taskManager = new TaskManager();
|
|
188
|
+
|
|
189
|
+
// Add tasks
|
|
190
|
+
taskManager.addTask(dataProcessor);
|
|
191
|
+
taskManager.addTask(deploymentTask);
|
|
192
|
+
|
|
193
|
+
// Schedule tasks with cron
|
|
194
|
+
taskManager.addAndScheduleTask(backupTask, '0 2 * * *'); // Daily at 2 AM
|
|
195
|
+
taskManager.addAndScheduleTask(healthCheck, '*/5 * * * *'); // Every 5 minutes
|
|
196
|
+
|
|
197
|
+
// Get task metadata
|
|
198
|
+
const metadata = taskManager.getTaskMetadata('DeployApplication');
|
|
199
|
+
console.log(metadata);
|
|
200
|
+
// {
|
|
201
|
+
// name: 'DeployApplication',
|
|
202
|
+
// status: 'idle' | 'running' | 'completed' | 'failed',
|
|
203
|
+
// steps: [...],
|
|
204
|
+
// currentProgress: 75,
|
|
205
|
+
// runCount: 12,
|
|
206
|
+
// lastRun: Date,
|
|
207
|
+
// buffered: false,
|
|
208
|
+
// bufferMax: undefined,
|
|
209
|
+
// version: '1.0.0',
|
|
210
|
+
// timeout: 30000
|
|
211
|
+
// }
|
|
212
|
+
|
|
213
|
+
// Get all scheduled tasks
|
|
214
|
+
const scheduled = taskManager.getScheduledTasks();
|
|
215
|
+
scheduled.forEach(task => {
|
|
216
|
+
console.log(`${task.name}: Next run at ${task.nextRun}`);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Execute and remove pattern
|
|
220
|
+
const report = await taskManager.addExecuteRemoveTask(temporaryTask, {
|
|
221
|
+
trackProgress: true
|
|
222
|
+
});
|
|
223
|
+
console.log(report);
|
|
224
|
+
// {
|
|
225
|
+
// taskName: 'TempTask',
|
|
226
|
+
// startTime: Date,
|
|
227
|
+
// endTime: Date,
|
|
228
|
+
// duration: 1523,
|
|
229
|
+
// steps: [...],
|
|
230
|
+
// stepsCompleted: ['step1', 'step2'],
|
|
231
|
+
// progress: 100,
|
|
232
|
+
// result: any,
|
|
233
|
+
// error?: Error
|
|
234
|
+
// }
|
|
235
|
+
```
|
|
505
236
|
|
|
506
|
-
|
|
507
|
-
|
|
237
|
+
## ๐จ Web Component Dashboard
|
|
238
|
+
|
|
239
|
+
Visualize your tasks in real-time with the included web component:
|
|
240
|
+
|
|
241
|
+
```html
|
|
242
|
+
<!DOCTYPE html>
|
|
243
|
+
<html>
|
|
244
|
+
<head>
|
|
245
|
+
<script type="module">
|
|
246
|
+
import { TaskManager } from '@push.rocks/taskbuffer';
|
|
247
|
+
import '@push.rocks/taskbuffer/dist_ts_web/taskbuffer-dashboard.js';
|
|
248
|
+
|
|
249
|
+
const taskManager = new TaskManager();
|
|
250
|
+
|
|
251
|
+
// Attach to dashboard
|
|
252
|
+
const dashboard = document.querySelector('taskbuffer-dashboard');
|
|
253
|
+
dashboard.taskManager = taskManager;
|
|
254
|
+
dashboard.refreshInterval = 500; // Update every 500ms
|
|
255
|
+
</script>
|
|
256
|
+
</head>
|
|
257
|
+
<body>
|
|
258
|
+
<taskbuffer-dashboard></taskbuffer-dashboard>
|
|
259
|
+
</body>
|
|
260
|
+
</html>
|
|
508
261
|
```
|
|
509
262
|
|
|
510
|
-
|
|
263
|
+
The dashboard provides:
|
|
264
|
+
- ๐ Real-time progress bars with step indicators
|
|
265
|
+
- ๐ Task execution history
|
|
266
|
+
- โฐ Scheduled task information
|
|
267
|
+
- ๐ฏ Interactive task controls
|
|
268
|
+
- ๐ Light/dark theme support
|
|
511
269
|
|
|
512
|
-
|
|
270
|
+
## ๐งฉ Advanced Patterns
|
|
513
271
|
|
|
514
|
-
|
|
515
|
-
import { TaskDebounced } from '@push.rocks/taskbuffer';
|
|
272
|
+
### Dynamic Task Routing
|
|
516
273
|
|
|
517
|
-
|
|
518
|
-
name: 'AutoSave',
|
|
519
|
-
taskFunction: async (content: string) => {
|
|
520
|
-
await saveToDatabase(content);
|
|
521
|
-
console.log('Content saved');
|
|
522
|
-
},
|
|
523
|
-
debounceTimeInMillis: 2000, // Wait 2 seconds of inactivity
|
|
524
|
-
});
|
|
274
|
+
Route tasks based on conditions:
|
|
525
275
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
276
|
+
```typescript
|
|
277
|
+
const routerTask = new Task({
|
|
278
|
+
name: 'Router',
|
|
279
|
+
taskFunction: async (request) => {
|
|
280
|
+
if (request.priority === 'high') {
|
|
281
|
+
return await expressProcessor.trigger(request);
|
|
282
|
+
} else if (request.size > 1000000) {
|
|
283
|
+
return await bulkProcessor.trigger(request);
|
|
284
|
+
} else {
|
|
285
|
+
return await standardProcessor.trigger(request);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
529
288
|
});
|
|
530
289
|
```
|
|
531
290
|
|
|
532
|
-
###
|
|
291
|
+
### Task Pools
|
|
533
292
|
|
|
534
|
-
|
|
293
|
+
Create reusable task pools for load distribution:
|
|
535
294
|
|
|
536
295
|
```typescript
|
|
537
|
-
|
|
296
|
+
class TaskPool {
|
|
297
|
+
private tasks: Task[] = [];
|
|
298
|
+
private currentIndex = 0;
|
|
299
|
+
|
|
300
|
+
constructor(poolSize: number, taskConfig: any) {
|
|
301
|
+
for (let i = 0; i < poolSize; i++) {
|
|
302
|
+
this.tasks.push(new Task({
|
|
303
|
+
...taskConfig,
|
|
304
|
+
name: `${taskConfig.name}_${i}`
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async execute(data: any) {
|
|
310
|
+
const task = this.tasks[this.currentIndex];
|
|
311
|
+
this.currentIndex = (this.currentIndex + 1) % this.tasks.length;
|
|
312
|
+
return await task.trigger(data);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
538
315
|
|
|
539
|
-
const
|
|
540
|
-
name: '
|
|
541
|
-
taskFunction: async () =>
|
|
542
|
-
await database.connect();
|
|
543
|
-
await cache.initialize();
|
|
544
|
-
await loadConfiguration();
|
|
545
|
-
console.log('System initialized');
|
|
546
|
-
},
|
|
316
|
+
const processorPool = new TaskPool(5, {
|
|
317
|
+
name: 'DataProcessor',
|
|
318
|
+
taskFunction: async (data) => processData(data)
|
|
547
319
|
});
|
|
548
|
-
|
|
549
|
-
// Safe to call multiple times - only runs once
|
|
550
|
-
await initTask.trigger();
|
|
551
|
-
await initTask.trigger(); // This won't run again
|
|
552
320
|
```
|
|
553
321
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
### Task Dependencies with Pre/Post Hooks
|
|
322
|
+
### Error Recovery & Retry
|
|
557
323
|
|
|
558
|
-
|
|
324
|
+
Implement robust error handling:
|
|
559
325
|
|
|
560
326
|
```typescript
|
|
561
|
-
const
|
|
562
|
-
name: '
|
|
563
|
-
taskFunction: async (data) => {
|
|
564
|
-
|
|
565
|
-
|
|
327
|
+
const resilientTask = new Task({
|
|
328
|
+
name: 'ResilientTask',
|
|
329
|
+
taskFunction: async (data, retryCount = 0) => {
|
|
330
|
+
try {
|
|
331
|
+
return await riskyOperation(data);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
if (retryCount < 3) {
|
|
334
|
+
console.log(`Retry ${retryCount + 1}/3`);
|
|
335
|
+
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, retryCount)));
|
|
336
|
+
return await resilientTask.trigger(data, retryCount + 1);
|
|
337
|
+
}
|
|
338
|
+
throw error;
|
|
566
339
|
}
|
|
567
|
-
|
|
568
|
-
},
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
const mainTask = new Task({
|
|
572
|
-
name: 'ProcessData',
|
|
573
|
-
taskFunction: async (data) => {
|
|
574
|
-
return await complexProcessing(data);
|
|
575
|
-
},
|
|
576
|
-
preTask: validationTask, // Runs before main task
|
|
577
|
-
afterTask: cleanupTask, // Runs after main task
|
|
340
|
+
}
|
|
578
341
|
});
|
|
579
342
|
```
|
|
580
343
|
|
|
581
|
-
### Task
|
|
344
|
+
### Task Composition
|
|
582
345
|
|
|
583
|
-
|
|
346
|
+
Compose complex behaviors from simple tasks:
|
|
584
347
|
|
|
585
348
|
```typescript
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
349
|
+
const compositeTask = new Task({
|
|
350
|
+
name: 'CompositeOperation',
|
|
351
|
+
steps: [
|
|
352
|
+
{ name: 'validate', description: 'Validating input', percentage: 20 },
|
|
353
|
+
{ name: 'process', description: 'Processing data', percentage: 60 },
|
|
354
|
+
{ name: 'notify', description: 'Sending notifications', percentage: 20 }
|
|
355
|
+
] as const,
|
|
356
|
+
taskFunction: async function(data) {
|
|
357
|
+
this.notifyStep('validate');
|
|
358
|
+
const validated = await validationTask.trigger(data);
|
|
359
|
+
|
|
360
|
+
this.notifyStep('process');
|
|
361
|
+
const processed = await processingTask.trigger(validated);
|
|
362
|
+
|
|
363
|
+
this.notifyStep('notify');
|
|
364
|
+
await notificationTask.trigger(processed);
|
|
365
|
+
|
|
366
|
+
return processed;
|
|
367
|
+
}
|
|
591
368
|
});
|
|
592
|
-
|
|
593
|
-
// Register tasks this runner can handle
|
|
594
|
-
runner.registerTask(dataProcessingTask);
|
|
595
|
-
runner.registerTask(imageResizeTask);
|
|
596
|
-
|
|
597
|
-
// Start processing
|
|
598
|
-
runner.start();
|
|
599
369
|
```
|
|
600
370
|
|
|
601
|
-
|
|
371
|
+
## ๐ง Configuration
|
|
602
372
|
|
|
603
|
-
|
|
373
|
+
### Task Options
|
|
604
374
|
|
|
605
375
|
```typescript
|
|
606
|
-
|
|
607
|
-
name
|
|
608
|
-
taskFunction:
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
376
|
+
interface TaskOptions<T = undefined, TSteps = []> {
|
|
377
|
+
name?: string; // Task identifier
|
|
378
|
+
taskFunction: Function; // Async function to execute
|
|
379
|
+
buffered?: boolean; // Enable buffering
|
|
380
|
+
bufferMax?: number; // Max concurrent executions
|
|
381
|
+
execDelay?: number; // Min delay between executions
|
|
382
|
+
timeout?: number; // Task timeout in ms
|
|
383
|
+
version?: string; // Task version
|
|
384
|
+
steps?: TSteps; // Progress steps configuration
|
|
385
|
+
taskSetup?: Function; // One-time setup function
|
|
386
|
+
beforeTask?: Function; // Runs before each execution
|
|
387
|
+
afterTask?: Function; // Runs after each execution
|
|
388
|
+
}
|
|
616
389
|
```
|
|
617
390
|
|
|
618
|
-
###
|
|
619
|
-
|
|
620
|
-
TaskBuffer automatically detects and prevents circular dependencies:
|
|
391
|
+
### TaskManager Options
|
|
621
392
|
|
|
622
393
|
```typescript
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
},
|
|
628
|
-
preTask: taskB, // This would create a cycle
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
const taskB = new Task({
|
|
632
|
-
name: 'TaskB',
|
|
633
|
-
taskFunction: async () => {
|
|
634
|
-
/* ... */
|
|
635
|
-
},
|
|
636
|
-
preTask: taskA, // Circular dependency detected!
|
|
394
|
+
const taskManager = new TaskManager({
|
|
395
|
+
maxConcurrentTasks: 10, // Global concurrency limit
|
|
396
|
+
defaultTimeout: 30000, // Default task timeout
|
|
397
|
+
logLevel: 'info' // Logging verbosity
|
|
637
398
|
});
|
|
638
399
|
```
|
|
639
400
|
|
|
640
|
-
|
|
401
|
+
## ๐ Monitoring & Observability
|
|
641
402
|
|
|
642
|
-
|
|
403
|
+
### Task Events
|
|
643
404
|
|
|
644
405
|
```typescript
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
name: step.name,
|
|
650
|
-
taskFunction: async (input) => {
|
|
651
|
-
return await processStep(step, input);
|
|
652
|
-
},
|
|
653
|
-
}),
|
|
654
|
-
);
|
|
655
|
-
|
|
656
|
-
const chain = new Taskchain({
|
|
657
|
-
name: 'DynamicWorkflow',
|
|
658
|
-
taskArray: tasks,
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
return await chain.trigger();
|
|
662
|
-
};
|
|
406
|
+
task.on('started', () => console.log('Task started'));
|
|
407
|
+
task.on('completed', (result) => console.log('Task completed:', result));
|
|
408
|
+
task.on('failed', (error) => console.error('Task failed:', error));
|
|
409
|
+
task.on('stepChange', (step) => console.log('Step:', step.name));
|
|
663
410
|
```
|
|
664
411
|
|
|
665
|
-
|
|
412
|
+
### Execution Metrics
|
|
666
413
|
|
|
667
|
-
|
|
414
|
+
```typescript
|
|
415
|
+
const metrics = task.getMetrics();
|
|
416
|
+
console.log({
|
|
417
|
+
totalRuns: metrics.runCount,
|
|
418
|
+
averageDuration: metrics.avgDuration,
|
|
419
|
+
successRate: metrics.successRate,
|
|
420
|
+
lastError: metrics.lastError
|
|
421
|
+
});
|
|
422
|
+
```
|
|
668
423
|
|
|
669
|
-
|
|
670
|
-
| -------------- | ---------- | ------------------------------ |
|
|
671
|
-
| `name` | `string` | Unique identifier for the task |
|
|
672
|
-
| `taskFunction` | `Function` | Async function to execute |
|
|
673
|
-
| `buffered` | `boolean` | Enable buffer management |
|
|
674
|
-
| `bufferMax` | `number` | Maximum concurrent executions |
|
|
675
|
-
| `execDelay` | `number` | Delay between executions (ms) |
|
|
676
|
-
| `timeout` | `number` | Task timeout (ms) |
|
|
677
|
-
| `preTask` | `Task` | Task to run before |
|
|
678
|
-
| `afterTask` | `Task` | Task to run after |
|
|
679
|
-
|
|
680
|
-
### TaskManager Methods
|
|
681
|
-
|
|
682
|
-
| Method | Description |
|
|
683
|
-
| ------------------------------- | ------------------------ |
|
|
684
|
-
| `addTask(task, cronExpression)` | Add and schedule a task |
|
|
685
|
-
| `removeTask(taskName)` | Remove a scheduled task |
|
|
686
|
-
| `start()` | Start the scheduler |
|
|
687
|
-
| `stop()` | Stop the scheduler |
|
|
688
|
-
| `getStats()` | Get execution statistics |
|
|
689
|
-
|
|
690
|
-
### Taskchain Methods
|
|
691
|
-
|
|
692
|
-
| Method | Description |
|
|
693
|
-
| ----------------------- | ---------------------- |
|
|
694
|
-
| `addTask(task)` | Add task to chain |
|
|
695
|
-
| `removeTask(taskName)` | Remove task from chain |
|
|
696
|
-
| `trigger(initialValue)` | Execute the chain |
|
|
697
|
-
| `reset()` | Reset chain state |
|
|
698
|
-
|
|
699
|
-
## Performance Tips ๐๏ธ
|
|
700
|
-
|
|
701
|
-
1. **Use buffering for I/O operations**: Prevents overwhelming external services
|
|
702
|
-
2. **Leverage parallel execution**: When tasks are independent, run them simultaneously
|
|
703
|
-
3. **Implement proper error handling**: Use try-catch in task functions
|
|
704
|
-
4. **Monitor task execution**: Use the built-in stats and logging
|
|
705
|
-
5. **Set appropriate timeouts**: Prevent hanging tasks from blocking your system
|
|
706
|
-
|
|
707
|
-
## Error Handling ๐ก๏ธ
|
|
424
|
+
## ๐งช Testing
|
|
708
425
|
|
|
709
426
|
```typescript
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
taskFunction: async (input) => {
|
|
713
|
-
try {
|
|
714
|
-
return await riskyOperation(input);
|
|
715
|
-
} catch (error) {
|
|
716
|
-
// Log error
|
|
717
|
-
console.error(`Task failed: ${error.message}`);
|
|
718
|
-
|
|
719
|
-
// Optionally retry
|
|
720
|
-
if (error.retryable) {
|
|
721
|
-
return await riskyOperation(input);
|
|
722
|
-
}
|
|
427
|
+
import { expect, tap } from '@git.zone/tstest';
|
|
428
|
+
import { Task } from '@push.rocks/taskbuffer';
|
|
723
429
|
|
|
724
|
-
|
|
725
|
-
|
|
430
|
+
tap.test('Task should execute with progress tracking', async () => {
|
|
431
|
+
const task = new Task({
|
|
432
|
+
name: 'TestTask',
|
|
433
|
+
steps: [
|
|
434
|
+
{ name: 'step1', description: 'Step 1', percentage: 50 },
|
|
435
|
+
{ name: 'step2', description: 'Step 2', percentage: 50 }
|
|
436
|
+
] as const,
|
|
437
|
+
taskFunction: async function() {
|
|
438
|
+
this.notifyStep('step1');
|
|
439
|
+
await new Promise(r => setTimeout(r, 100));
|
|
440
|
+
this.notifyStep('step2');
|
|
441
|
+
return 'done';
|
|
726
442
|
}
|
|
727
|
-
}
|
|
728
|
-
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const result = await task.trigger();
|
|
446
|
+
expect(result).toEqual('done');
|
|
447
|
+
expect(task.getProgress()).toEqual(100);
|
|
729
448
|
});
|
|
730
449
|
```
|
|
731
450
|
|
|
732
|
-
## Real-World Examples
|
|
451
|
+
## ๐ Real-World Examples
|
|
733
452
|
|
|
734
|
-
### API Rate
|
|
453
|
+
### API Rate Limiter
|
|
735
454
|
|
|
736
455
|
```typescript
|
|
737
|
-
const
|
|
738
|
-
name: '
|
|
739
|
-
taskFunction: async (endpoint: string) => {
|
|
740
|
-
return await fetch(`https://api.example.com${endpoint}`);
|
|
741
|
-
},
|
|
456
|
+
const apiLimiter = new Task({
|
|
457
|
+
name: 'APIRateLimiter',
|
|
742
458
|
buffered: true,
|
|
743
|
-
bufferMax: 10,
|
|
744
|
-
execDelay: 100, //
|
|
459
|
+
bufferMax: 10, // Max 10 requests per second
|
|
460
|
+
execDelay: 100, // 100ms between requests
|
|
461
|
+
taskFunction: async (endpoint, data) => {
|
|
462
|
+
return await fetch(endpoint, {
|
|
463
|
+
method: 'POST',
|
|
464
|
+
body: JSON.stringify(data)
|
|
465
|
+
});
|
|
466
|
+
}
|
|
745
467
|
});
|
|
746
468
|
```
|
|
747
469
|
|
|
@@ -750,63 +472,119 @@ const apiClient = new Task({
|
|
|
750
472
|
```typescript
|
|
751
473
|
const migrationChain = new Taskchain({
|
|
752
474
|
name: 'DatabaseMigration',
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
]
|
|
760
|
-
});
|
|
475
|
+
tasks: [
|
|
476
|
+
backupDatabaseTask,
|
|
477
|
+
validateSchemaTask,
|
|
478
|
+
runMigrationsTask,
|
|
479
|
+
verifyIntegrityTask,
|
|
480
|
+
updateIndexesTask
|
|
481
|
+
]
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Execute with rollback on failure
|
|
485
|
+
try {
|
|
486
|
+
await migrationChain.trigger();
|
|
487
|
+
console.log('Migration successful!');
|
|
488
|
+
} catch (error) {
|
|
489
|
+
await rollbackTask.trigger();
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
761
492
|
```
|
|
762
493
|
|
|
763
|
-
###
|
|
494
|
+
### Distributed Job Queue
|
|
764
495
|
|
|
765
496
|
```typescript
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
services.forEach((service) => {
|
|
769
|
-
const healthCheck = new Task({
|
|
770
|
-
name: `HealthCheck:${service.name}`,
|
|
771
|
-
taskFunction: async () => {
|
|
772
|
-
const healthy = await checkHealth(service.url);
|
|
773
|
-
if (!healthy) {
|
|
774
|
-
await alertOps(service);
|
|
775
|
-
}
|
|
776
|
-
},
|
|
777
|
-
});
|
|
497
|
+
const jobQueue = new TaskManager();
|
|
778
498
|
|
|
779
|
-
|
|
499
|
+
// Worker tasks
|
|
500
|
+
const imageProcessor = new Task({
|
|
501
|
+
name: 'ImageProcessor',
|
|
502
|
+
buffered: true,
|
|
503
|
+
bufferMax: 5,
|
|
504
|
+
steps: [
|
|
505
|
+
{ name: 'download', description: 'Downloading', percentage: 20 },
|
|
506
|
+
{ name: 'resize', description: 'Resizing', percentage: 40 },
|
|
507
|
+
{ name: 'optimize', description: 'Optimizing', percentage: 30 },
|
|
508
|
+
{ name: 'upload', description: 'Uploading', percentage: 10 }
|
|
509
|
+
] as const,
|
|
510
|
+
taskFunction: async function(job) {
|
|
511
|
+
this.notifyStep('download');
|
|
512
|
+
const image = await downloadImage(job.url);
|
|
513
|
+
|
|
514
|
+
this.notifyStep('resize');
|
|
515
|
+
const resized = await resizeImage(image, job.dimensions);
|
|
516
|
+
|
|
517
|
+
this.notifyStep('optimize');
|
|
518
|
+
const optimized = await optimizeImage(resized);
|
|
519
|
+
|
|
520
|
+
this.notifyStep('upload');
|
|
521
|
+
return await uploadToCDN(optimized);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
jobQueue.addTask(imageProcessor);
|
|
526
|
+
|
|
527
|
+
// Process incoming jobs
|
|
528
|
+
messageQueue.on('job', async (job) => {
|
|
529
|
+
const result = await jobQueue.getTaskByName('ImageProcessor').trigger(job);
|
|
530
|
+
await messageQueue.ack(job.id, result);
|
|
780
531
|
});
|
|
781
532
|
```
|
|
782
533
|
|
|
783
|
-
##
|
|
534
|
+
## ๐ Performance Tips
|
|
535
|
+
|
|
536
|
+
1. **Use Buffering Wisely** - Enable buffering for I/O-bound tasks
|
|
537
|
+
2. **Set Appropriate Delays** - Use `execDelay` to prevent API rate limits
|
|
538
|
+
3. **Leverage Task Pools** - Distribute load across multiple task instances
|
|
539
|
+
4. **Monitor Progress** - Use step tracking for long-running operations
|
|
540
|
+
5. **Clean Up** - Use `addExecuteRemoveTask` for one-time operations
|
|
541
|
+
|
|
542
|
+
## ๐ Debugging
|
|
543
|
+
|
|
544
|
+
Enable detailed logging:
|
|
784
545
|
|
|
785
546
|
```typescript
|
|
786
|
-
import {
|
|
787
|
-
import { Task } from '@push.rocks/taskbuffer';
|
|
547
|
+
import { logger } from '@push.rocks/smartlog';
|
|
788
548
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
expect(result).toEqual(expectedValue);
|
|
792
|
-
});
|
|
549
|
+
logger.enableConsole();
|
|
550
|
+
logger.level = 'debug';
|
|
793
551
|
|
|
794
|
-
|
|
552
|
+
// Tasks will now output detailed execution logs
|
|
795
553
|
```
|
|
796
554
|
|
|
797
|
-
##
|
|
555
|
+
## ๐ API Reference
|
|
556
|
+
|
|
557
|
+
### Core Classes
|
|
558
|
+
|
|
559
|
+
- **`Task<T, TSteps>`** - Basic task unit with optional step tracking
|
|
560
|
+
- **`TaskManager`** - Central orchestrator for task management
|
|
561
|
+
- **`Taskchain`** - Sequential task executor
|
|
562
|
+
- **`TaskParallel`** - Concurrent task executor
|
|
563
|
+
- **`TaskOnce`** - Single-execution task
|
|
564
|
+
- **`TaskLoop`** - Repeating task with conditions
|
|
798
565
|
|
|
799
|
-
|
|
566
|
+
### Key Methods
|
|
800
567
|
|
|
801
|
-
|
|
568
|
+
#### Task Methods
|
|
569
|
+
- `trigger(input?: T): Promise<any>` - Execute the task
|
|
570
|
+
- `notifyStep(stepName: StepNames<TSteps>): void` - Update current step
|
|
571
|
+
- `getProgress(): number` - Get progress percentage (0-100)
|
|
572
|
+
- `getStepsMetadata(): ITaskStep[]` - Get detailed step information
|
|
573
|
+
- `getMetadata(): ITaskMetadata` - Get complete task metadata
|
|
802
574
|
|
|
803
|
-
|
|
804
|
-
-
|
|
805
|
-
-
|
|
575
|
+
#### TaskManager Methods
|
|
576
|
+
- `addTask(task: Task): void` - Register a task
|
|
577
|
+
- `getTaskByName(name: string): Task | undefined` - Retrieve task by name
|
|
578
|
+
- `addAndScheduleTask(task: Task, cronExpression: string): void` - Schedule task
|
|
579
|
+
- `descheduleTaskByName(name: string): void` - Remove scheduling
|
|
580
|
+
- `getTaskMetadata(name: string): ITaskMetadata | null` - Get task metadata
|
|
581
|
+
- `getAllTasksMetadata(): ITaskMetadata[]` - Get all tasks metadata
|
|
582
|
+
- `getScheduledTasks(): IScheduledTaskInfo[]` - List scheduled tasks
|
|
583
|
+
- `addExecuteRemoveTask(task, options?): Promise<ITaskExecutionReport>` - Execute once
|
|
806
584
|
|
|
807
585
|
## License and Legal Information
|
|
808
586
|
|
|
809
|
-
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
587
|
+
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
810
588
|
|
|
811
589
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
812
590
|
|
|
@@ -821,4 +599,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
|
|
|
821
599
|
|
|
822
600
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
|
823
601
|
|
|
824
|
-
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
|
602
|
+
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|