494 lines
13 KiB
Markdown
494 lines
13 KiB
Markdown
# @push.rocks/taskbuffer 🚀
|
|
|
|
A **powerful**, **flexible**, and **TypeScript-first** task management library for orchestrating asynchronous operations with style. From simple task execution to complex distributed workflows, taskbuffer has got you covered.
|
|
|
|
## Install 📦
|
|
|
|
```bash
|
|
npm install @push.rocks/taskbuffer --save
|
|
```
|
|
|
|
Or with **pnpm** (recommended):
|
|
|
|
```bash
|
|
pnpm add @push.rocks/taskbuffer
|
|
```
|
|
|
|
## Why taskbuffer? 🤔
|
|
|
|
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:
|
|
|
|
- **🎯 TypeScript-first**: Built with TypeScript for TypeScript - enjoy complete type safety and excellent IDE support
|
|
- **⚡ Flexible execution**: From simple tasks to complex parallel workflows with dependencies
|
|
- **🔄 Smart buffering**: Control concurrent executions with intelligent buffer management
|
|
- **⏰ Built-in scheduling**: Cron-based task scheduling without additional dependencies
|
|
- **🎭 Multiple paradigms**: Support for debounced, throttled, and one-time execution patterns
|
|
- **🔌 Extensible**: Clean architecture that's easy to extend and customize
|
|
- **🏃 Zero dependencies on external schedulers**: Everything you need is included
|
|
|
|
## Core Concepts 🎓
|
|
|
|
### Task
|
|
The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control.
|
|
|
|
### Taskchain
|
|
Sequential task execution - tasks run one after another, with results passed along the chain.
|
|
|
|
### Taskparallel
|
|
Parallel task execution - multiple tasks run simultaneously for maximum performance.
|
|
|
|
### TaskManager
|
|
Centralized task scheduling and management using cron expressions.
|
|
|
|
### TaskDebounced
|
|
Debounced task execution - prevents rapid repeated executions, only running after a quiet period.
|
|
|
|
### TaskOnce
|
|
Singleton task execution - ensures a task runs exactly once, perfect for initialization routines.
|
|
|
|
## Quick Start 🏁
|
|
|
|
### Basic Task Execution
|
|
|
|
```typescript
|
|
import { Task } from '@push.rocks/taskbuffer';
|
|
|
|
// Create a simple task
|
|
const myTask = new Task({
|
|
name: 'DataProcessor',
|
|
taskFunction: async () => {
|
|
const data = await fetchData();
|
|
return processData(data);
|
|
}
|
|
});
|
|
|
|
// Execute the task
|
|
const result = await myTask.trigger();
|
|
```
|
|
|
|
### Buffered Execution (Rate Limiting)
|
|
|
|
Perfect for API calls or database operations that need throttling:
|
|
|
|
```typescript
|
|
const apiTask = new Task({
|
|
name: 'APICall',
|
|
taskFunction: async (endpoint: string) => {
|
|
return await fetch(endpoint);
|
|
},
|
|
buffered: true,
|
|
bufferMax: 3, // Maximum 3 concurrent executions
|
|
execDelay: 1000 // Wait 1 second between executions
|
|
});
|
|
|
|
// These will be automatically throttled
|
|
for (let i = 0; i < 10; i++) {
|
|
apiTask.trigger(`/api/data/${i}`);
|
|
}
|
|
```
|
|
|
|
### Task Chains - Sequential Workflows
|
|
|
|
Build complex workflows where each step depends on the previous:
|
|
|
|
```typescript
|
|
import { Task, Taskchain } from '@push.rocks/taskbuffer';
|
|
|
|
const fetchTask = new Task({
|
|
name: 'FetchData',
|
|
taskFunction: async () => {
|
|
const response = await fetch('/api/data');
|
|
return response.json();
|
|
}
|
|
});
|
|
|
|
const transformTask = new Task({
|
|
name: 'TransformData',
|
|
taskFunction: async (data) => {
|
|
return data.map(item => ({
|
|
...item,
|
|
processed: true,
|
|
timestamp: Date.now()
|
|
}));
|
|
}
|
|
});
|
|
|
|
const saveTask = new Task({
|
|
name: 'SaveData',
|
|
taskFunction: async (transformedData) => {
|
|
await database.bulkInsert(transformedData);
|
|
return { saved: transformedData.length };
|
|
}
|
|
});
|
|
|
|
const workflow = new Taskchain({
|
|
name: 'DataPipeline',
|
|
taskArray: [fetchTask, transformTask, saveTask]
|
|
});
|
|
|
|
// Execute the entire chain
|
|
const result = await workflow.trigger();
|
|
console.log(`Processed ${result.saved} items`);
|
|
```
|
|
|
|
### Parallel Execution - Maximum Performance
|
|
|
|
Execute multiple independent tasks simultaneously:
|
|
|
|
```typescript
|
|
import { Task, Taskparallel } from '@push.rocks/taskbuffer';
|
|
|
|
const tasks = ['user', 'posts', 'comments'].map(resource =>
|
|
new Task({
|
|
name: `Fetch${resource}`,
|
|
taskFunction: async () => {
|
|
const data = await fetch(`/api/${resource}`);
|
|
return data.json();
|
|
}
|
|
})
|
|
);
|
|
|
|
const parallelFetch = new Taskparallel({
|
|
taskArray: tasks
|
|
});
|
|
|
|
// All tasks execute simultaneously
|
|
const [users, posts, comments] = await parallelFetch.trigger();
|
|
```
|
|
|
|
### Scheduled Tasks with TaskManager
|
|
|
|
Run tasks on a schedule using cron expressions:
|
|
|
|
```typescript
|
|
import { Task, TaskManager } from '@push.rocks/taskbuffer';
|
|
|
|
const backupTask = new Task({
|
|
name: 'DatabaseBackup',
|
|
taskFunction: async () => {
|
|
await performBackup();
|
|
console.log(`Backup completed at ${new Date().toISOString()}`);
|
|
}
|
|
});
|
|
|
|
const manager = new TaskManager();
|
|
|
|
// Add and schedule tasks
|
|
manager.addAndScheduleTask(backupTask, '0 0 * * *'); // Daily at midnight
|
|
manager.addAndScheduleTask(healthCheck, '*/5 * * * *'); // Every 5 minutes
|
|
|
|
// Start the scheduler
|
|
manager.start();
|
|
|
|
// Later... stop if needed
|
|
manager.stop();
|
|
```
|
|
|
|
### Debounced Tasks - Smart Throttling
|
|
|
|
Prevent task spam with intelligent debouncing:
|
|
|
|
```typescript
|
|
import { TaskDebounced } from '@push.rocks/taskbuffer';
|
|
|
|
const saveTask = new TaskDebounced({
|
|
name: 'AutoSave',
|
|
taskFunction: async (content: string) => {
|
|
await saveToDatabase(content);
|
|
console.log('Content saved');
|
|
},
|
|
debounceTimeInMillis: 2000 // Wait 2 seconds of inactivity
|
|
});
|
|
|
|
// Rapid calls will be debounced
|
|
input.addEventListener('input', (e) => {
|
|
saveTask.trigger(e.target.value);
|
|
});
|
|
```
|
|
|
|
### One-Time Tasks - Initialize Once
|
|
|
|
Ensure initialization code runs exactly once:
|
|
|
|
```typescript
|
|
import { TaskOnce } from '@push.rocks/taskbuffer';
|
|
|
|
const initTask = new TaskOnce({
|
|
name: 'SystemInitialization',
|
|
taskFunction: async () => {
|
|
await database.connect();
|
|
await cache.initialize();
|
|
await loadConfiguration();
|
|
console.log('System initialized');
|
|
}
|
|
});
|
|
|
|
// Safe to call multiple times - only runs once
|
|
await initTask.trigger();
|
|
await initTask.trigger(); // This won't run again
|
|
```
|
|
|
|
## Advanced Features 🔥
|
|
|
|
### Task Dependencies with Pre/Post Hooks
|
|
|
|
Create sophisticated task relationships:
|
|
|
|
```typescript
|
|
const validationTask = new Task({
|
|
name: 'ValidateInput',
|
|
taskFunction: async (data) => {
|
|
if (!isValid(data)) {
|
|
throw new Error('Validation failed');
|
|
}
|
|
return data;
|
|
}
|
|
});
|
|
|
|
const mainTask = new Task({
|
|
name: 'ProcessData',
|
|
taskFunction: async (data) => {
|
|
return await complexProcessing(data);
|
|
},
|
|
preTask: validationTask, // Runs before main task
|
|
afterTask: cleanupTask // Runs after main task
|
|
});
|
|
```
|
|
|
|
### Task Runners - Distributed Execution
|
|
|
|
The TaskRunner system enables distributed task execution across multiple workers:
|
|
|
|
```typescript
|
|
import { TaskRunner } from '@push.rocks/taskbuffer';
|
|
|
|
const runner = new TaskRunner({
|
|
name: 'WorkerNode1',
|
|
maxConcurrentTasks: 5
|
|
});
|
|
|
|
// Register tasks this runner can handle
|
|
runner.registerTask(dataProcessingTask);
|
|
runner.registerTask(imageResizeTask);
|
|
|
|
// Start processing
|
|
runner.start();
|
|
```
|
|
|
|
### Buffer Management Strategies
|
|
|
|
Fine-tune concurrent execution behavior:
|
|
|
|
```typescript
|
|
const task = new Task({
|
|
name: 'ResourceIntensive',
|
|
taskFunction: async () => { /* ... */ },
|
|
buffered: true,
|
|
bufferMax: 5, // Max 5 concurrent
|
|
execDelay: 100, // 100ms between starts
|
|
timeout: 30000 // 30 second timeout
|
|
});
|
|
```
|
|
|
|
### Cycle Detection and Prevention
|
|
|
|
TaskBuffer automatically detects and prevents circular dependencies:
|
|
|
|
```typescript
|
|
const taskA = new Task({
|
|
name: 'TaskA',
|
|
taskFunction: async () => { /* ... */ },
|
|
preTask: taskB // This would create a cycle
|
|
});
|
|
|
|
const taskB = new Task({
|
|
name: 'TaskB',
|
|
taskFunction: async () => { /* ... */ },
|
|
preTask: taskA // Circular dependency detected!
|
|
});
|
|
```
|
|
|
|
### Dynamic Task Creation
|
|
|
|
Create tasks on-the-fly based on runtime conditions:
|
|
|
|
```typescript
|
|
const dynamicWorkflow = async (config: Config) => {
|
|
const tasks = config.steps.map(step =>
|
|
new Task({
|
|
name: step.name,
|
|
taskFunction: async (input) => {
|
|
return await processStep(step, input);
|
|
}
|
|
})
|
|
);
|
|
|
|
const chain = new Taskchain({
|
|
name: 'DynamicWorkflow',
|
|
taskArray: tasks
|
|
});
|
|
|
|
return await chain.trigger();
|
|
};
|
|
```
|
|
|
|
## API Reference 📚
|
|
|
|
### Task Options
|
|
|
|
| Option | Type | Description |
|
|
|--------|------|-------------|
|
|
| `name` | `string` | Unique identifier for the task |
|
|
| `taskFunction` | `Function` | Async function to execute |
|
|
| `buffered` | `boolean` | Enable buffer management |
|
|
| `bufferMax` | `number` | Maximum concurrent executions |
|
|
| `execDelay` | `number` | Delay between executions (ms) |
|
|
| `timeout` | `number` | Task timeout (ms) |
|
|
| `preTask` | `Task` | Task to run before |
|
|
| `afterTask` | `Task` | Task to run after |
|
|
|
|
### TaskManager Methods
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `addTask(task, cronExpression)` | Add and schedule a task |
|
|
| `removeTask(taskName)` | Remove a scheduled task |
|
|
| `start()` | Start the scheduler |
|
|
| `stop()` | Stop the scheduler |
|
|
| `getStats()` | Get execution statistics |
|
|
|
|
### Taskchain Methods
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `addTask(task)` | Add task to chain |
|
|
| `removeTask(taskName)` | Remove task from chain |
|
|
| `trigger(initialValue)` | Execute the chain |
|
|
| `reset()` | Reset chain state |
|
|
|
|
## Performance Tips 🏎️
|
|
|
|
1. **Use buffering for I/O operations**: Prevents overwhelming external services
|
|
2. **Leverage parallel execution**: When tasks are independent, run them simultaneously
|
|
3. **Implement proper error handling**: Use try-catch in task functions
|
|
4. **Monitor task execution**: Use the built-in stats and logging
|
|
5. **Set appropriate timeouts**: Prevent hanging tasks from blocking your system
|
|
|
|
## Error Handling 🛡️
|
|
|
|
```typescript
|
|
const robustTask = new Task({
|
|
name: 'RobustOperation',
|
|
taskFunction: async (input) => {
|
|
try {
|
|
return await riskyOperation(input);
|
|
} catch (error) {
|
|
// Log error
|
|
console.error(`Task failed: ${error.message}`);
|
|
|
|
// Optionally retry
|
|
if (error.retryable) {
|
|
return await riskyOperation(input);
|
|
}
|
|
|
|
// Or return default value
|
|
return defaultValue;
|
|
}
|
|
},
|
|
timeout: 5000 // Fail if takes longer than 5 seconds
|
|
});
|
|
```
|
|
|
|
## Real-World Examples 🌍
|
|
|
|
### API Rate Limiting
|
|
|
|
```typescript
|
|
const apiClient = new Task({
|
|
name: 'RateLimitedAPI',
|
|
taskFunction: async (endpoint: string) => {
|
|
return await fetch(`https://api.example.com${endpoint}`);
|
|
},
|
|
buffered: true,
|
|
bufferMax: 10, // 10 requests
|
|
execDelay: 100 // Per 100ms = 100 req/s max
|
|
});
|
|
```
|
|
|
|
### Database Migration Pipeline
|
|
|
|
```typescript
|
|
const migrationChain = new Taskchain({
|
|
name: 'DatabaseMigration',
|
|
taskArray: [
|
|
backupTask,
|
|
schemaUpdateTask,
|
|
dataTransformTask,
|
|
validationTask,
|
|
cleanupTask
|
|
]
|
|
});
|
|
```
|
|
|
|
### Microservice Health Monitoring
|
|
|
|
```typescript
|
|
const healthMonitor = new TaskManager();
|
|
|
|
services.forEach(service => {
|
|
const healthCheck = new Task({
|
|
name: `HealthCheck:${service.name}`,
|
|
taskFunction: async () => {
|
|
const healthy = await checkHealth(service.url);
|
|
if (!healthy) {
|
|
await alertOps(service);
|
|
}
|
|
}
|
|
});
|
|
|
|
healthMonitor.addAndScheduleTask(healthCheck, '*/1 * * * *'); // Every minute
|
|
});
|
|
```
|
|
|
|
## Testing 🧪
|
|
|
|
```typescript
|
|
import { expect, tap } from '@git.zone/tstest';
|
|
import { Task } from '@push.rocks/taskbuffer';
|
|
|
|
tap.test('should execute task successfully', async () => {
|
|
const result = await myTask.trigger();
|
|
expect(result).toEqual(expectedValue);
|
|
});
|
|
|
|
tap.start();
|
|
```
|
|
|
|
## Contributing 🤝
|
|
|
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
|
|
## Support 💬
|
|
|
|
- 📧 Email: [hello@task.vc](mailto:hello@task.vc)
|
|
- 🐛 Issues: [GitHub Issues](https://github.com/push-rocks/taskbuffer/issues)
|
|
- 📖 Docs: [Documentation](https://code.foss.global/push.rocks/taskbuffer)
|
|
|
|
## License and Legal Information
|
|
|
|
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.
|
|
|
|
**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.
|
|
|
|
### Trademarks
|
|
|
|
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
|
|
|
### Company Information
|
|
|
|
Task Venture Capital GmbH
|
|
Registered at District court Bremen HRB 35230 HB, Germany
|
|
|
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
|
|
|
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. |