Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
6c9b975029 | |||
b1725cbdf9 | |||
d54012379c | |||
dc47bc3d2a |
@@ -6,8 +6,8 @@ on:
|
|||||||
- '**'
|
- '**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Install pnpm and npmci
|
- name: Install pnpm and npmci
|
||||||
run: |
|
run: |
|
||||||
pnpm install -g pnpm
|
pnpm install -g pnpm
|
||||||
pnpm install -g @shipzone/npmci
|
pnpm install -g @ship.zone/npmci
|
||||||
|
|
||||||
- name: Run npm prepare
|
- name: Run npm prepare
|
||||||
run: npmci npm prepare
|
run: npmci npm prepare
|
||||||
|
@@ -6,8 +6,8 @@ on:
|
|||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
pnpm install -g pnpm
|
pnpm install -g pnpm
|
||||||
pnpm install -g @shipzone/npmci
|
pnpm install -g @ship.zone/npmci
|
||||||
npmci npm prepare
|
npmci npm prepare
|
||||||
|
|
||||||
- name: Audit production dependencies
|
- name: Audit production dependencies
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
pnpm install -g pnpm
|
pnpm install -g pnpm
|
||||||
pnpm install -g @shipzone/npmci
|
pnpm install -g @ship.zone/npmci
|
||||||
npmci npm prepare
|
npmci npm prepare
|
||||||
|
|
||||||
- name: Test stable
|
- name: Test stable
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
pnpm install -g pnpm
|
pnpm install -g pnpm
|
||||||
pnpm install -g @shipzone/npmci
|
pnpm install -g @ship.zone/npmci
|
||||||
npmci npm prepare
|
npmci npm prepare
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
@@ -104,7 +104,7 @@ jobs:
|
|||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
pnpm install -g pnpm
|
pnpm install -g pnpm
|
||||||
pnpm install -g @shipzone/npmci
|
pnpm install -g @ship.zone/npmci
|
||||||
npmci npm prepare
|
npmci npm prepare
|
||||||
|
|
||||||
- name: Code quality
|
- name: Code quality
|
||||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -3,7 +3,6 @@
|
|||||||
# artifacts
|
# artifacts
|
||||||
coverage/
|
coverage/
|
||||||
public/
|
public/
|
||||||
pages/
|
|
||||||
|
|
||||||
# installs
|
# installs
|
||||||
node_modules/
|
node_modules/
|
||||||
@@ -17,4 +16,8 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
dist_*/
|
dist_*/
|
||||||
|
|
||||||
# custom
|
# AI
|
||||||
|
.claude/
|
||||||
|
.serena/
|
||||||
|
|
||||||
|
#------# custom
|
Binary file not shown.
33
changelog.md
33
changelog.md
@@ -1,6 +1,28 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-08-26 - 3.1.10 - fix(task)
|
||||||
|
Implement core Task execution flow, buffering and lifecycle; update README with generics and buffer docs
|
||||||
|
|
||||||
|
- Implement Task.runTask including preTask/afterTask chaining, touched-task cycle prevention and error handling.
|
||||||
|
- Add Task helpers: extractTask, isTask, isTaskTouched and emptyTaskFunction (resolved promise).
|
||||||
|
- Introduce task lifecycle coordination: finished promise, resolveFinished, and blockingTasks to await dependent tasks.
|
||||||
|
- Support taskSetup/setupValue, execDelay handling, and wait-for-blocking-tasks before execution.
|
||||||
|
- Wire up trigger() to choose buffered vs unbuffered execution (triggerBuffered / triggerUnBuffered) and integrate BufferRunner.
|
||||||
|
- Improve logging and safer promise handling (caught errors are logged).
|
||||||
|
- Update README with extended TypeScript generics examples and expanded buffer behavior and strategies documentation.
|
||||||
|
|
||||||
|
## 2025-08-26 - 3.1.9 - fix(tests)
|
||||||
|
Update CI workflows, fix tests and refresh README/package metadata
|
||||||
|
|
||||||
|
- CI: switch Docker image to code.foss.global/host.today/ht-docker-node:npmci and adjust NPMCI_COMPUTED_REPOURL; replace npmci installer package name from @shipzone/npmci to @ship.zone/npmci in Gitea workflows
|
||||||
|
- Tests: update test imports to use @git.zone/tstest/tapbundle and apply small formatting fixes to test files
|
||||||
|
- Package metadata: update bugs URL and homepage to code.foss.global, add a pnpm.overrides placeholder in package.json
|
||||||
|
- .gitignore: add AI/tooling directories (.claude, .serena) and reorganize custom section
|
||||||
|
- Code style/TS fixes: minor formatting changes across ts sources (trailing commas, line breaks, consistent object/argument commas) and small API surface formatting fixes
|
||||||
|
- Documentation: whitespace/formatting cleanups in README and add changelog entry for 3.1.8
|
||||||
|
|
||||||
## 2025-08-26 - 3.1.8 - fix(tests)
|
## 2025-08-26 - 3.1.8 - fix(tests)
|
||||||
|
|
||||||
Update test runner and imports, refresh README and package metadata, add project tooling/config files
|
Update test runner and imports, refresh README and package metadata, add project tooling/config files
|
||||||
|
|
||||||
- Replaced test imports from '@push.rocks/tapbundle' to '@git.zone/tstest/tapbundle' across test files
|
- Replaced test imports from '@push.rocks/tapbundle' to '@git.zone/tstest/tapbundle' across test files
|
||||||
@@ -11,6 +33,7 @@ Update test runner and imports, refresh README and package metadata, add project
|
|||||||
- Added development/project tooling and metadata files (.claude settings, .serena project/memories) to aid local development and CI
|
- Added development/project tooling and metadata files (.claude settings, .serena project/memories) to aid local development and CI
|
||||||
|
|
||||||
## 2024-05-29 - 3.1.7 - maintenance/config
|
## 2024-05-29 - 3.1.7 - maintenance/config
|
||||||
|
|
||||||
Updated package metadata and build configuration.
|
Updated package metadata and build configuration.
|
||||||
|
|
||||||
- Updated package description.
|
- Updated package description.
|
||||||
@@ -18,36 +41,42 @@ Updated package metadata and build configuration.
|
|||||||
- Updated npmextra.json githost entries (changes across 2024-03-30, 2024-04-01, 2024-04-14).
|
- Updated npmextra.json githost entries (changes across 2024-03-30, 2024-04-01, 2024-04-14).
|
||||||
|
|
||||||
## 2023-08-04 - 3.0.15 - feat(Task)
|
## 2023-08-04 - 3.0.15 - feat(Task)
|
||||||
|
|
||||||
Tasks can now be blocked by other tasks.
|
Tasks can now be blocked by other tasks.
|
||||||
|
|
||||||
- Introduced task blocking support in the Task implementation.
|
- Introduced task blocking support in the Task implementation.
|
||||||
- Release contains related maintenance and patch fixes.
|
- Release contains related maintenance and patch fixes.
|
||||||
|
|
||||||
## 2023-01-07 to 2023-10-20 - 3.0.4..3.1.6 - maintenance
|
## 2023-01-07 to 2023-10-20 - 3.0.4..3.1.6 - maintenance
|
||||||
|
|
||||||
Series of patch releases focused on core fixes and stability.
|
Series of patch releases focused on core fixes and stability.
|
||||||
|
|
||||||
- Numerous core fixes and small adjustments across many patch versions.
|
- Numerous core fixes and small adjustments across many patch versions.
|
||||||
- General maintenance: bug fixes, internal updates and stability improvements.
|
- General maintenance: bug fixes, internal updates and stability improvements.
|
||||||
|
|
||||||
## 2022-03-25 - 2.1.17 - BREAKING(core)
|
## 2022-03-25 - 2.1.17 - BREAKING(core)
|
||||||
|
|
||||||
Switched module format to ESM (breaking).
|
Switched module format to ESM (breaking).
|
||||||
|
|
||||||
- BREAKING CHANGE: project now uses ESM module format.
|
- BREAKING CHANGE: project now uses ESM module format.
|
||||||
- Release includes the version bump and migration to ESM.
|
- Release includes the version bump and migration to ESM.
|
||||||
|
|
||||||
## 2019-11-28 - 2.0.16 - feat(taskrunner)
|
## 2019-11-28 - 2.0.16 - feat(taskrunner)
|
||||||
|
|
||||||
Introduce a working task runner.
|
Introduce a working task runner.
|
||||||
|
|
||||||
- Added/activated a working taskrunner implementation.
|
- Added/activated a working taskrunner implementation.
|
||||||
- Improvements to task execution and orchestration.
|
- Improvements to task execution and orchestration.
|
||||||
|
|
||||||
## 2019-09-05 to 2022-11-14 - 2.0.3..2.1.16 - maintenance
|
## 2019-09-05 to 2022-11-14 - 2.0.3..2.1.16 - maintenance
|
||||||
|
|
||||||
Ongoing maintenance and incremental fixes between 2.0.x and 2.1.x series.
|
Ongoing maintenance and incremental fixes between 2.0.x and 2.1.x series.
|
||||||
|
|
||||||
- Multiple fixes labeled as core maintenance updates.
|
- Multiple fixes labeled as core maintenance updates.
|
||||||
- CI, packaging and small doc/test fixes rolled out across these releases.
|
- CI, packaging and small doc/test fixes rolled out across these releases.
|
||||||
|
|
||||||
## 2018-08-04 - 2.0.0 - major
|
## 2018-08-04 - 2.0.0 - major
|
||||||
|
|
||||||
Major release and scope change with CI/test updates.
|
Major release and scope change with CI/test updates.
|
||||||
|
|
||||||
- Released 2.0.0 with updated docs.
|
- Released 2.0.0 with updated docs.
|
||||||
@@ -55,6 +84,7 @@ Major release and scope change with CI/test updates.
|
|||||||
- CI and testing updates (moved to new tstest), package.json adjustments.
|
- CI and testing updates (moved to new tstest), package.json adjustments.
|
||||||
|
|
||||||
## 2017-07-12 - 1.0.21 - enhancements
|
## 2017-07-12 - 1.0.21 - enhancements
|
||||||
|
|
||||||
Feature additions around task utilities and manager.
|
Feature additions around task utilities and manager.
|
||||||
|
|
||||||
- Introduced TaskOnce.
|
- Introduced TaskOnce.
|
||||||
@@ -63,18 +93,21 @@ Feature additions around task utilities and manager.
|
|||||||
- Documentation and test improvements.
|
- Documentation and test improvements.
|
||||||
|
|
||||||
## 2016-08-03 - 1.0.6 - types
|
## 2016-08-03 - 1.0.6 - types
|
||||||
|
|
||||||
Type and promise improvements.
|
Type and promise improvements.
|
||||||
|
|
||||||
- Now returns correct Promise types.
|
- Now returns correct Promise types.
|
||||||
- Dependency and typings updates.
|
- Dependency and typings updates.
|
||||||
|
|
||||||
## 2016-08-01 - 1.0.0 - stable
|
## 2016-08-01 - 1.0.0 - stable
|
||||||
|
|
||||||
First stable 1.0.0 release.
|
First stable 1.0.0 release.
|
||||||
|
|
||||||
- Exported public interfaces.
|
- Exported public interfaces.
|
||||||
- Base API stabilized for 1.x line.
|
- Base API stabilized for 1.x line.
|
||||||
|
|
||||||
## 2016-05-15 to 2016-05-06 - 0.1.0..0.0.5 - initial features
|
## 2016-05-15 to 2016-05-06 - 0.1.0..0.0.5 - initial features
|
||||||
|
|
||||||
Initial implementation of core task primitives and utilities.
|
Initial implementation of core task primitives and utilities.
|
||||||
|
|
||||||
- Added Taskparallel class to execute tasks in parallel.
|
- Added Taskparallel class to execute tasks in parallel.
|
||||||
|
11
package.json
11
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/taskbuffer",
|
"name": "@push.rocks/taskbuffer",
|
||||||
"version": "3.1.8",
|
"version": "3.1.10",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.",
|
"description": "A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
@@ -30,9 +30,9 @@
|
|||||||
"author": "Lossless GmbH",
|
"author": "Lossless GmbH",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://gitlab.com/pushrocks/taskbuffer/issues"
|
"url": "https://code.foss.global/push.rocks/taskbuffer/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://code.foss.global/push.rocks/taskbuffer",
|
"homepage": "https://code.foss.global/push.rocks/taskbuffer#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@push.rocks/lik": "^6.0.5",
|
"@push.rocks/lik": "^6.0.5",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
@@ -64,5 +64,8 @@
|
|||||||
"browserslist": [
|
"browserslist": [
|
||||||
"last 1 chrome versions"
|
"last 1 chrome versions"
|
||||||
],
|
],
|
||||||
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
|
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748",
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
404
readme.md
404
readme.md
@@ -29,21 +29,27 @@ In the modern JavaScript ecosystem, managing asynchronous tasks efficiently is c
|
|||||||
## Core Concepts 🎓
|
## Core Concepts 🎓
|
||||||
|
|
||||||
### Task
|
### Task
|
||||||
|
|
||||||
The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control.
|
The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control.
|
||||||
|
|
||||||
### Taskchain
|
### Taskchain
|
||||||
|
|
||||||
Sequential task execution - tasks run one after another, with results passed along the chain.
|
Sequential task execution - tasks run one after another, with results passed along the chain.
|
||||||
|
|
||||||
### Taskparallel
|
### Taskparallel
|
||||||
|
|
||||||
Parallel task execution - multiple tasks run simultaneously for maximum performance.
|
Parallel task execution - multiple tasks run simultaneously for maximum performance.
|
||||||
|
|
||||||
### TaskManager
|
### TaskManager
|
||||||
|
|
||||||
Centralized task scheduling and management using cron expressions.
|
Centralized task scheduling and management using cron expressions.
|
||||||
|
|
||||||
### TaskDebounced
|
### TaskDebounced
|
||||||
|
|
||||||
Debounced task execution - prevents rapid repeated executions, only running after a quiet period.
|
Debounced task execution - prevents rapid repeated executions, only running after a quiet period.
|
||||||
|
|
||||||
### TaskOnce
|
### TaskOnce
|
||||||
|
|
||||||
Singleton task execution - ensures a task runs exactly once, perfect for initialization routines.
|
Singleton task execution - ensures a task runs exactly once, perfect for initialization routines.
|
||||||
|
|
||||||
## Quick Start 🏁
|
## Quick Start 🏁
|
||||||
@@ -59,13 +65,329 @@ const myTask = new Task({
|
|||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
const data = await fetchData();
|
const data = await fetchData();
|
||||||
return processData(data);
|
return processData(data);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Execute the task
|
// Execute the task
|
||||||
const result = await myTask.trigger();
|
const result = await myTask.trigger();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## TypeScript Generics Support 🔬
|
||||||
|
|
||||||
|
TaskBuffer leverages TypeScript's powerful generics system for complete type safety across your task chains and workflows.
|
||||||
|
|
||||||
|
### Generic Task Functions
|
||||||
|
|
||||||
|
Tasks support generic type parameters for both input and output types:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Task, ITaskFunction } from '@push.rocks/taskbuffer';
|
||||||
|
|
||||||
|
// Define typed interfaces
|
||||||
|
interface UserData {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessedUser {
|
||||||
|
userId: string;
|
||||||
|
displayName: string;
|
||||||
|
normalized: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create strongly typed tasks
|
||||||
|
const processUserTask = new Task<UserData, ProcessedUser>({
|
||||||
|
name: 'ProcessUser',
|
||||||
|
taskFunction: async (user: UserData): Promise<ProcessedUser> => {
|
||||||
|
return {
|
||||||
|
userId: user.id,
|
||||||
|
displayName: user.name.toUpperCase(),
|
||||||
|
normalized: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Type safety enforced at compile time
|
||||||
|
const result: ProcessedUser = await processUserTask.trigger({
|
||||||
|
id: '123',
|
||||||
|
name: 'John Doe',
|
||||||
|
email: 'john@example.com'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generic Setup Values
|
||||||
|
|
||||||
|
Tasks can accept setup values through generics, perfect for configuration:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface TaskConfig {
|
||||||
|
apiEndpoint: string;
|
||||||
|
retryCount: number;
|
||||||
|
timeout: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configuredTask = new Task<TaskConfig>({
|
||||||
|
name: 'ConfiguredTask',
|
||||||
|
taskSetup: async () => ({
|
||||||
|
apiEndpoint: 'https://api.example.com',
|
||||||
|
retryCount: 3,
|
||||||
|
timeout: 5000
|
||||||
|
}),
|
||||||
|
taskFunction: async (data: any, setupValue: TaskConfig) => {
|
||||||
|
// setupValue is fully typed!
|
||||||
|
for (let i = 0; i < setupValue.retryCount; i++) {
|
||||||
|
try {
|
||||||
|
return await fetchWithTimeout(
|
||||||
|
setupValue.apiEndpoint,
|
||||||
|
setupValue.timeout
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (i === setupValue.retryCount - 1) throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type-Safe Task Chains
|
||||||
|
|
||||||
|
Chain tasks with preserved type flow:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Each task knows its input and output types
|
||||||
|
const fetchTask = new Task<void, UserData[]>({
|
||||||
|
name: 'FetchUsers',
|
||||||
|
taskFunction: async (): Promise<UserData[]> => {
|
||||||
|
return await api.getUsers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterTask = new Task<UserData[], UserData[]>({
|
||||||
|
name: 'FilterActive',
|
||||||
|
taskFunction: async (users: UserData[]): Promise<UserData[]> => {
|
||||||
|
return users.filter(user => user.isActive);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapTask = new Task<UserData[], ProcessedUser[]>({
|
||||||
|
name: 'MapToProcessed',
|
||||||
|
taskFunction: async (users: UserData[]): Promise<ProcessedUser[]> => {
|
||||||
|
return users.map(transformUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Type safety flows through the chain
|
||||||
|
const chain = new Taskchain({
|
||||||
|
name: 'UserPipeline',
|
||||||
|
taskArray: [fetchTask, filterTask, mapTask]
|
||||||
|
});
|
||||||
|
|
||||||
|
const finalResult: ProcessedUser[] = await chain.trigger();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Buffer Behavior Deep Dive 🌊
|
||||||
|
|
||||||
|
The buffer system in TaskBuffer provides intelligent control over concurrent executions, preventing system overload while maximizing throughput.
|
||||||
|
|
||||||
|
### How Buffering Works
|
||||||
|
|
||||||
|
When a task is buffered, TaskBuffer manages a queue of executions:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const bufferedTask = new Task({
|
||||||
|
name: 'BufferedOperation',
|
||||||
|
taskFunction: async (data) => {
|
||||||
|
console.log(`Processing: ${data}`);
|
||||||
|
await simulateWork();
|
||||||
|
return `Processed: ${data}`;
|
||||||
|
},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 3 // Maximum 3 concurrent executions
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger 10 executions rapidly
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
bufferedTask.trigger(`Item ${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// What happens:
|
||||||
|
// 1. First 3 tasks start immediately
|
||||||
|
// 2. Items 4-10 are queued
|
||||||
|
// 3. As each task completes, next queued item starts
|
||||||
|
// 4. Never more than 3 tasks running simultaneously
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buffer Truncation Behavior
|
||||||
|
|
||||||
|
When buffer limit is reached, new calls are intelligently managed:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const truncatingTask = new Task({
|
||||||
|
name: 'TruncatingBuffer',
|
||||||
|
taskFunction: async (data) => {
|
||||||
|
await processData(data);
|
||||||
|
},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 5 // Maximum 5 in buffer
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rapid fire 100 calls
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
truncatingTask.trigger(`Data ${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer behavior:
|
||||||
|
// - First 5 calls: Added to buffer and start processing
|
||||||
|
// - Calls 6-100: Each overwrites the 5th buffer slot
|
||||||
|
// - Result: Only processes items 0,1,2,3, and 99 (last one)
|
||||||
|
// - This prevents memory overflow in high-frequency scenarios
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Buffer Strategies
|
||||||
|
|
||||||
|
#### 1. **Sliding Window Buffer**
|
||||||
|
Perfect for real-time data processing where only recent items matter:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const slidingWindowTask = new Task({
|
||||||
|
name: 'SlidingWindow',
|
||||||
|
taskFunction: async (data) => {
|
||||||
|
return await analyzeRecentData(data);
|
||||||
|
},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 10, // Keep last 10 items
|
||||||
|
execDelay: 100 // Process every 100ms
|
||||||
|
});
|
||||||
|
|
||||||
|
// In a real-time stream scenario
|
||||||
|
dataStream.on('data', (chunk) => {
|
||||||
|
slidingWindowTask.trigger(chunk);
|
||||||
|
// Older items automatically dropped when buffer full
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. **Throttled Buffer**
|
||||||
|
Combine buffering with execution delays for rate limiting:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const apiRateLimiter = new Task({
|
||||||
|
name: 'RateLimitedAPI',
|
||||||
|
taskFunction: async (request) => {
|
||||||
|
return await api.call(request);
|
||||||
|
},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 10, // Max 10 queued requests
|
||||||
|
execDelay: 1000 // 1 second between executions
|
||||||
|
});
|
||||||
|
|
||||||
|
// Requests are queued and executed at 1/second
|
||||||
|
// Prevents API rate limit violations
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. **Priority Buffer** (Custom Implementation)
|
||||||
|
Implement priority queuing with buffer management:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class PriorityBufferedTask extends Task {
|
||||||
|
private priorityQueue: Array<{data: any, priority: number}> = [];
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
super({
|
||||||
|
...options,
|
||||||
|
taskFunction: async (item) => {
|
||||||
|
// Process based on priority
|
||||||
|
return await this.processByPriority(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerWithPriority(data: any, priority: number) {
|
||||||
|
if (this.priorityQueue.length >= this.bufferMax) {
|
||||||
|
// Remove lowest priority item if buffer full
|
||||||
|
this.priorityQueue.sort((a, b) => b.priority - a.priority);
|
||||||
|
this.priorityQueue.pop();
|
||||||
|
}
|
||||||
|
this.priorityQueue.push({data, priority});
|
||||||
|
this.priorityQueue.sort((a, b) => b.priority - a.priority);
|
||||||
|
return this.trigger(this.priorityQueue.shift());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buffer Monitoring
|
||||||
|
|
||||||
|
Track buffer utilization and performance:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const monitoredTask = new Task({
|
||||||
|
name: 'MonitoredBuffer',
|
||||||
|
taskFunction: async (data) => {
|
||||||
|
const startTime = Date.now();
|
||||||
|
const result = await processData(data);
|
||||||
|
console.log(`Processing time: ${Date.now() - startTime}ms`);
|
||||||
|
console.log(`Buffer utilization: ${monitoredTask.bufferRunner.bufferCounter}/${monitoredTask.bufferMax}`);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 20
|
||||||
|
});
|
||||||
|
|
||||||
|
// Monitor buffer saturation
|
||||||
|
setInterval(() => {
|
||||||
|
const utilization = (monitoredTask.bufferRunner.bufferCounter / monitoredTask.bufferMax) * 100;
|
||||||
|
if (utilization > 80) {
|
||||||
|
console.warn(`Buffer near capacity: ${utilization.toFixed(1)}%`);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buffer Best Practices
|
||||||
|
|
||||||
|
1. **Choose appropriate buffer sizes**:
|
||||||
|
- I/O operations: 5-10 concurrent
|
||||||
|
- CPU-intensive: Number of cores
|
||||||
|
- API calls: Based on rate limits
|
||||||
|
|
||||||
|
2. **Handle buffer overflow gracefully**:
|
||||||
|
```typescript
|
||||||
|
const task = new Task({
|
||||||
|
taskFunction: async (data) => {
|
||||||
|
try {
|
||||||
|
return await process(data);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'BUFFER_OVERFLOW') {
|
||||||
|
// Implement backoff strategy
|
||||||
|
await delay(1000);
|
||||||
|
return task.trigger(data);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 10
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Monitor and adjust dynamically**:
|
||||||
|
```typescript
|
||||||
|
// Adjust buffer size based on system load
|
||||||
|
const adaptiveTask = new Task({
|
||||||
|
name: 'AdaptiveBuffer',
|
||||||
|
taskFunction: async (data) => {
|
||||||
|
const cpuLoad = await getSystemLoad();
|
||||||
|
if (cpuLoad > 0.8) {
|
||||||
|
adaptiveTask.bufferMax = Math.max(2, adaptiveTask.bufferMax - 1);
|
||||||
|
} else if (cpuLoad < 0.5) {
|
||||||
|
adaptiveTask.bufferMax = Math.min(20, adaptiveTask.bufferMax + 1);
|
||||||
|
}
|
||||||
|
return await process(data);
|
||||||
|
},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 10
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### Buffered Execution (Rate Limiting)
|
### Buffered Execution (Rate Limiting)
|
||||||
|
|
||||||
Perfect for API calls or database operations that need throttling:
|
Perfect for API calls or database operations that need throttling:
|
||||||
@@ -78,7 +400,7 @@ const apiTask = new Task({
|
|||||||
},
|
},
|
||||||
buffered: true,
|
buffered: true,
|
||||||
bufferMax: 3, // Maximum 3 concurrent executions
|
bufferMax: 3, // Maximum 3 concurrent executions
|
||||||
execDelay: 1000 // Wait 1 second between executions
|
execDelay: 1000, // Wait 1 second between executions
|
||||||
});
|
});
|
||||||
|
|
||||||
// These will be automatically throttled
|
// These will be automatically throttled
|
||||||
@@ -99,18 +421,18 @@ const fetchTask = new Task({
|
|||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
const response = await fetch('/api/data');
|
const response = await fetch('/api/data');
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const transformTask = new Task({
|
const transformTask = new Task({
|
||||||
name: 'TransformData',
|
name: 'TransformData',
|
||||||
taskFunction: async (data) => {
|
taskFunction: async (data) => {
|
||||||
return data.map(item => ({
|
return data.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
processed: true,
|
processed: true,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now(),
|
||||||
}));
|
}));
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const saveTask = new Task({
|
const saveTask = new Task({
|
||||||
@@ -118,12 +440,12 @@ const saveTask = new Task({
|
|||||||
taskFunction: async (transformedData) => {
|
taskFunction: async (transformedData) => {
|
||||||
await database.bulkInsert(transformedData);
|
await database.bulkInsert(transformedData);
|
||||||
return { saved: transformedData.length };
|
return { saved: transformedData.length };
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const workflow = new Taskchain({
|
const workflow = new Taskchain({
|
||||||
name: 'DataPipeline',
|
name: 'DataPipeline',
|
||||||
taskArray: [fetchTask, transformTask, saveTask]
|
taskArray: [fetchTask, transformTask, saveTask],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Execute the entire chain
|
// Execute the entire chain
|
||||||
@@ -138,18 +460,19 @@ Execute multiple independent tasks simultaneously:
|
|||||||
```typescript
|
```typescript
|
||||||
import { Task, Taskparallel } from '@push.rocks/taskbuffer';
|
import { Task, Taskparallel } from '@push.rocks/taskbuffer';
|
||||||
|
|
||||||
const tasks = ['user', 'posts', 'comments'].map(resource =>
|
const tasks = ['user', 'posts', 'comments'].map(
|
||||||
|
(resource) =>
|
||||||
new Task({
|
new Task({
|
||||||
name: `Fetch${resource}`,
|
name: `Fetch${resource}`,
|
||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
const data = await fetch(`/api/${resource}`);
|
const data = await fetch(`/api/${resource}`);
|
||||||
return data.json();
|
return data.json();
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const parallelFetch = new Taskparallel({
|
const parallelFetch = new Taskparallel({
|
||||||
taskArray: tasks
|
taskArray: tasks,
|
||||||
});
|
});
|
||||||
|
|
||||||
// All tasks execute simultaneously
|
// All tasks execute simultaneously
|
||||||
@@ -168,7 +491,7 @@ const backupTask = new Task({
|
|||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
await performBackup();
|
await performBackup();
|
||||||
console.log(`Backup completed at ${new Date().toISOString()}`);
|
console.log(`Backup completed at ${new Date().toISOString()}`);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const manager = new TaskManager();
|
const manager = new TaskManager();
|
||||||
@@ -197,7 +520,7 @@ const saveTask = new TaskDebounced({
|
|||||||
await saveToDatabase(content);
|
await saveToDatabase(content);
|
||||||
console.log('Content saved');
|
console.log('Content saved');
|
||||||
},
|
},
|
||||||
debounceTimeInMillis: 2000 // Wait 2 seconds of inactivity
|
debounceTimeInMillis: 2000, // Wait 2 seconds of inactivity
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rapid calls will be debounced
|
// Rapid calls will be debounced
|
||||||
@@ -220,7 +543,7 @@ const initTask = new TaskOnce({
|
|||||||
await cache.initialize();
|
await cache.initialize();
|
||||||
await loadConfiguration();
|
await loadConfiguration();
|
||||||
console.log('System initialized');
|
console.log('System initialized');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Safe to call multiple times - only runs once
|
// Safe to call multiple times - only runs once
|
||||||
@@ -242,7 +565,7 @@ const validationTask = new Task({
|
|||||||
throw new Error('Validation failed');
|
throw new Error('Validation failed');
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const mainTask = new Task({
|
const mainTask = new Task({
|
||||||
@@ -251,7 +574,7 @@ const mainTask = new Task({
|
|||||||
return await complexProcessing(data);
|
return await complexProcessing(data);
|
||||||
},
|
},
|
||||||
preTask: validationTask, // Runs before main task
|
preTask: validationTask, // Runs before main task
|
||||||
afterTask: cleanupTask // Runs after main task
|
afterTask: cleanupTask, // Runs after main task
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -264,7 +587,7 @@ import { TaskRunner } from '@push.rocks/taskbuffer';
|
|||||||
|
|
||||||
const runner = new TaskRunner({
|
const runner = new TaskRunner({
|
||||||
name: 'WorkerNode1',
|
name: 'WorkerNode1',
|
||||||
maxConcurrentTasks: 5
|
maxConcurrentTasks: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register tasks this runner can handle
|
// Register tasks this runner can handle
|
||||||
@@ -282,11 +605,13 @@ Fine-tune concurrent execution behavior:
|
|||||||
```typescript
|
```typescript
|
||||||
const task = new Task({
|
const task = new Task({
|
||||||
name: 'ResourceIntensive',
|
name: 'ResourceIntensive',
|
||||||
taskFunction: async () => { /* ... */ },
|
taskFunction: async () => {
|
||||||
|
/* ... */
|
||||||
|
},
|
||||||
buffered: true,
|
buffered: true,
|
||||||
bufferMax: 5, // Max 5 concurrent
|
bufferMax: 5, // Max 5 concurrent
|
||||||
execDelay: 100, // 100ms between starts
|
execDelay: 100, // 100ms between starts
|
||||||
timeout: 30000 // 30 second timeout
|
timeout: 30000, // 30 second timeout
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -297,14 +622,18 @@ TaskBuffer automatically detects and prevents circular dependencies:
|
|||||||
```typescript
|
```typescript
|
||||||
const taskA = new Task({
|
const taskA = new Task({
|
||||||
name: 'TaskA',
|
name: 'TaskA',
|
||||||
taskFunction: async () => { /* ... */ },
|
taskFunction: async () => {
|
||||||
preTask: taskB // This would create a cycle
|
/* ... */
|
||||||
|
},
|
||||||
|
preTask: taskB, // This would create a cycle
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskB = new Task({
|
const taskB = new Task({
|
||||||
name: 'TaskB',
|
name: 'TaskB',
|
||||||
taskFunction: async () => { /* ... */ },
|
taskFunction: async () => {
|
||||||
preTask: taskA // Circular dependency detected!
|
/* ... */
|
||||||
|
},
|
||||||
|
preTask: taskA, // Circular dependency detected!
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -314,18 +643,19 @@ Create tasks on-the-fly based on runtime conditions:
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const dynamicWorkflow = async (config: Config) => {
|
const dynamicWorkflow = async (config: Config) => {
|
||||||
const tasks = config.steps.map(step =>
|
const tasks = config.steps.map(
|
||||||
|
(step) =>
|
||||||
new Task({
|
new Task({
|
||||||
name: step.name,
|
name: step.name,
|
||||||
taskFunction: async (input) => {
|
taskFunction: async (input) => {
|
||||||
return await processStep(step, input);
|
return await processStep(step, input);
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const chain = new Taskchain({
|
const chain = new Taskchain({
|
||||||
name: 'DynamicWorkflow',
|
name: 'DynamicWorkflow',
|
||||||
taskArray: tasks
|
taskArray: tasks,
|
||||||
});
|
});
|
||||||
|
|
||||||
return await chain.trigger();
|
return await chain.trigger();
|
||||||
@@ -337,7 +667,7 @@ const dynamicWorkflow = async (config: Config) => {
|
|||||||
### Task Options
|
### Task Options
|
||||||
|
|
||||||
| Option | Type | Description |
|
| Option | Type | Description |
|
||||||
|--------|------|-------------|
|
| -------------- | ---------- | ------------------------------ |
|
||||||
| `name` | `string` | Unique identifier for the task |
|
| `name` | `string` | Unique identifier for the task |
|
||||||
| `taskFunction` | `Function` | Async function to execute |
|
| `taskFunction` | `Function` | Async function to execute |
|
||||||
| `buffered` | `boolean` | Enable buffer management |
|
| `buffered` | `boolean` | Enable buffer management |
|
||||||
@@ -350,7 +680,7 @@ const dynamicWorkflow = async (config: Config) => {
|
|||||||
### TaskManager Methods
|
### TaskManager Methods
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
|--------|-------------|
|
| ------------------------------- | ------------------------ |
|
||||||
| `addTask(task, cronExpression)` | Add and schedule a task |
|
| `addTask(task, cronExpression)` | Add and schedule a task |
|
||||||
| `removeTask(taskName)` | Remove a scheduled task |
|
| `removeTask(taskName)` | Remove a scheduled task |
|
||||||
| `start()` | Start the scheduler |
|
| `start()` | Start the scheduler |
|
||||||
@@ -360,7 +690,7 @@ const dynamicWorkflow = async (config: Config) => {
|
|||||||
### Taskchain Methods
|
### Taskchain Methods
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
|--------|-------------|
|
| ----------------------- | ---------------------- |
|
||||||
| `addTask(task)` | Add task to chain |
|
| `addTask(task)` | Add task to chain |
|
||||||
| `removeTask(taskName)` | Remove task from chain |
|
| `removeTask(taskName)` | Remove task from chain |
|
||||||
| `trigger(initialValue)` | Execute the chain |
|
| `trigger(initialValue)` | Execute the chain |
|
||||||
@@ -395,7 +725,7 @@ const robustTask = new Task({
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
timeout: 5000 // Fail if takes longer than 5 seconds
|
timeout: 5000, // Fail if takes longer than 5 seconds
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -411,7 +741,7 @@ const apiClient = new Task({
|
|||||||
},
|
},
|
||||||
buffered: true,
|
buffered: true,
|
||||||
bufferMax: 10, // 10 requests
|
bufferMax: 10, // 10 requests
|
||||||
execDelay: 100 // Per 100ms = 100 req/s max
|
execDelay: 100, // Per 100ms = 100 req/s max
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -425,8 +755,8 @@ const migrationChain = new Taskchain({
|
|||||||
schemaUpdateTask,
|
schemaUpdateTask,
|
||||||
dataTransformTask,
|
dataTransformTask,
|
||||||
validationTask,
|
validationTask,
|
||||||
cleanupTask
|
cleanupTask,
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -435,7 +765,7 @@ const migrationChain = new Taskchain({
|
|||||||
```typescript
|
```typescript
|
||||||
const healthMonitor = new TaskManager();
|
const healthMonitor = new TaskManager();
|
||||||
|
|
||||||
services.forEach(service => {
|
services.forEach((service) => {
|
||||||
const healthCheck = new Task({
|
const healthCheck = new Task({
|
||||||
name: `HealthCheck:${service.name}`,
|
name: `HealthCheck:${service.name}`,
|
||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
@@ -443,7 +773,7 @@ services.forEach(service => {
|
|||||||
if (!healthy) {
|
if (!healthy) {
|
||||||
await alertOps(service);
|
await alertOps(service);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
healthMonitor.addAndScheduleTask(healthCheck, '*/1 * * * *'); // Every minute
|
healthMonitor.addAndScheduleTask(healthCheck, '*/1 * * * *'); // Every minute
|
||||||
|
@@ -49,10 +49,12 @@ tap.test('should execute setup function before the task function', async () => {
|
|||||||
const task2 = new taskbuffer.Task({
|
const task2 = new taskbuffer.Task({
|
||||||
name: 'Task 2',
|
name: 'Task 2',
|
||||||
taskSetup: async () => {
|
taskSetup: async () => {
|
||||||
console.log('this is the setup function for task 2. It should only run once.')
|
console.log(
|
||||||
|
'this is the setup function for task 2. It should only run once.',
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
nice: 'yes',
|
nice: 'yes',
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
taskFunction: async (before, setupArg) => {
|
taskFunction: async (before, setupArg) => {
|
||||||
expect(setupArg).toEqual({ nice: 'yes' });
|
expect(setupArg).toEqual({ nice: 'yes' });
|
||||||
|
@@ -29,7 +29,7 @@ tap.test('should run the task as expected', async () => {
|
|||||||
taskDone.resolve();
|
taskDone.resolve();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
myTaskManager.start();
|
myTaskManager.start();
|
||||||
await myTaskManager.triggerTaskByName('myTask');
|
await myTaskManager.triggerTaskByName('myTask');
|
||||||
|
@@ -16,7 +16,7 @@ tap.test('should execute task when its scheduled', async (tools) => {
|
|||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
console.log('hi');
|
console.log('hi');
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
testTaskRunner.addTask(
|
testTaskRunner.addTask(
|
||||||
@@ -25,7 +25,7 @@ tap.test('should execute task when its scheduled', async (tools) => {
|
|||||||
console.log('there');
|
console.log('there');
|
||||||
done.resolve();
|
done.resolve();
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await done.promise;
|
await done.promise;
|
||||||
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/taskbuffer',
|
name: '@push.rocks/taskbuffer',
|
||||||
version: '3.1.8',
|
version: '3.1.10',
|
||||||
description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
|
description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
|
||||||
}
|
}
|
||||||
|
@@ -13,9 +13,8 @@ export class BufferRunner {
|
|||||||
if (!(this.bufferCounter >= this.task.bufferMax)) {
|
if (!(this.bufferCounter >= this.task.bufferMax)) {
|
||||||
this.bufferCounter++;
|
this.bufferCounter++;
|
||||||
}
|
}
|
||||||
const returnPromise: Promise<any> = this.task.cycleCounter.getPromiseForCycle(
|
const returnPromise: Promise<any> =
|
||||||
this.bufferCounter
|
this.task.cycleCounter.getPromiseForCycle(this.bufferCounter);
|
||||||
);
|
|
||||||
if (!this.task.running) {
|
if (!this.task.running) {
|
||||||
this._run(x);
|
this._run(x);
|
||||||
}
|
}
|
||||||
|
@@ -26,11 +26,11 @@ export interface IDistributedTaskRequestResult {
|
|||||||
|
|
||||||
export abstract class AbstractDistributedCoordinator {
|
export abstract class AbstractDistributedCoordinator {
|
||||||
public abstract fireDistributedTaskRequest(
|
public abstract fireDistributedTaskRequest(
|
||||||
infoBasis: IDistributedTaskRequest
|
infoBasis: IDistributedTaskRequest,
|
||||||
): Promise<IDistributedTaskRequestResult>;
|
): Promise<IDistributedTaskRequestResult>;
|
||||||
|
|
||||||
public abstract updateDistributedTaskRequest(
|
public abstract updateDistributedTaskRequest(
|
||||||
infoBasis: IDistributedTaskRequest
|
infoBasis: IDistributedTaskRequest,
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
|
||||||
public abstract start(): Promise<void>;
|
public abstract start(): Promise<void>;
|
||||||
|
@@ -16,7 +16,7 @@ export type TPreOrAfterTaskFunction = () => Task<any>;
|
|||||||
|
|
||||||
export class Task<T = undefined> {
|
export class Task<T = undefined> {
|
||||||
public static extractTask<T = undefined>(
|
public static extractTask<T = undefined>(
|
||||||
preOrAfterTaskArg: Task<T> | TPreOrAfterTaskFunction
|
preOrAfterTaskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||||
): Task<T> {
|
): Task<T> {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case !preOrAfterTaskArg:
|
case !preOrAfterTaskArg:
|
||||||
@@ -47,7 +47,7 @@ export class Task<T = undefined> {
|
|||||||
|
|
||||||
public static isTaskTouched<T = undefined>(
|
public static isTaskTouched<T = undefined>(
|
||||||
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||||
touchedTasksArray: Task<T>[]
|
touchedTasksArray: Task<T>[],
|
||||||
): boolean {
|
): boolean {
|
||||||
const taskToCheck = Task.extractTask(taskArg);
|
const taskToCheck = Task.extractTask(taskArg);
|
||||||
let result = false;
|
let result = false;
|
||||||
@@ -61,7 +61,7 @@ export class Task<T = undefined> {
|
|||||||
|
|
||||||
public static runTask = async <T>(
|
public static runTask = async <T>(
|
||||||
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||||
optionsArg: { x?: any; touchedTasksArray?: Task<T>[] }
|
optionsArg: { x?: any; touchedTasksArray?: Task<T>[] },
|
||||||
) => {
|
) => {
|
||||||
const taskToRun = Task.extractTask(taskArg);
|
const taskToRun = Task.extractTask(taskArg);
|
||||||
const done = plugins.smartpromise.defer();
|
const done = plugins.smartpromise.defer();
|
||||||
@@ -105,7 +105,10 @@ export class Task<T = undefined> {
|
|||||||
const localDeferred = plugins.smartpromise.defer();
|
const localDeferred = plugins.smartpromise.defer();
|
||||||
localDeferred.promise
|
localDeferred.promise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (taskToRun.preTask && !Task.isTaskTouched(taskToRun.preTask, touchedTasksArray)) {
|
if (
|
||||||
|
taskToRun.preTask &&
|
||||||
|
!Task.isTaskTouched(taskToRun.preTask, touchedTasksArray)
|
||||||
|
) {
|
||||||
return Task.runTask(taskToRun.preTask, { x, touchedTasksArray });
|
return Task.runTask(taskToRun.preTask, { x, touchedTasksArray });
|
||||||
} else {
|
} else {
|
||||||
const done2 = plugins.smartpromise.defer();
|
const done2 = plugins.smartpromise.defer();
|
||||||
@@ -121,8 +124,14 @@ export class Task<T = undefined> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((x) => {
|
.then((x) => {
|
||||||
if (taskToRun.afterTask && !Task.isTaskTouched(taskToRun.afterTask, touchedTasksArray)) {
|
if (
|
||||||
return Task.runTask(taskToRun.afterTask, { x: x, touchedTasksArray: touchedTasksArray });
|
taskToRun.afterTask &&
|
||||||
|
!Task.isTaskTouched(taskToRun.afterTask, touchedTasksArray)
|
||||||
|
) {
|
||||||
|
return Task.runTask(taskToRun.afterTask, {
|
||||||
|
x: x,
|
||||||
|
touchedTasksArray: touchedTasksArray,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const done2 = plugins.smartpromise.defer();
|
const done2 = plugins.smartpromise.defer();
|
||||||
done2.resolve(x);
|
done2.resolve(x);
|
||||||
|
@@ -27,14 +27,18 @@ export class Taskchain extends Task {
|
|||||||
let taskCounter = 0; // counter for iterating async over the taskArray
|
let taskCounter = 0; // counter for iterating async over the taskArray
|
||||||
const iterateTasks = (x: any) => {
|
const iterateTasks = (x: any) => {
|
||||||
if (typeof this.taskArray[taskCounter] !== 'undefined') {
|
if (typeof this.taskArray[taskCounter] !== 'undefined') {
|
||||||
console.log(this.name + ' running: Task' + this.taskArray[taskCounter].name);
|
console.log(
|
||||||
|
this.name + ' running: Task' + this.taskArray[taskCounter].name,
|
||||||
|
);
|
||||||
this.taskArray[taskCounter].trigger(x).then((x) => {
|
this.taskArray[taskCounter].trigger(x).then((x) => {
|
||||||
logger.log('info', this.taskArray[taskCounter].name);
|
logger.log('info', this.taskArray[taskCounter].name);
|
||||||
taskCounter++;
|
taskCounter++;
|
||||||
iterateTasks(x);
|
iterateTasks(x);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log('Taskchain "' + this.name + '" completed successfully');
|
console.log(
|
||||||
|
'Taskchain "' + this.name + '" completed successfully',
|
||||||
|
);
|
||||||
done.resolve(x);
|
done.resolve(x);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -19,7 +19,9 @@ export class TaskDebounced<T = unknown> extends Task {
|
|||||||
});
|
});
|
||||||
this.taskFunction = optionsArg.taskFunction;
|
this.taskFunction = optionsArg.taskFunction;
|
||||||
this._observableIntake.observable
|
this._observableIntake.observable
|
||||||
.pipe(plugins.smartrx.rxjs.ops.debounceTime(optionsArg.debounceTimeInMillis))
|
.pipe(
|
||||||
|
plugins.smartrx.rxjs.ops.debounceTime(optionsArg.debounceTimeInMillis),
|
||||||
|
)
|
||||||
.subscribe((x) => {
|
.subscribe((x) => {
|
||||||
this.taskFunction(x);
|
this.taskFunction(x);
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import * as plugins from './taskbuffer.plugins.js';
|
import * as plugins from './taskbuffer.plugins.js';
|
||||||
import { Task } from './taskbuffer.classes.task.js';
|
import { Task } from './taskbuffer.classes.task.js';
|
||||||
import { AbstractDistributedCoordinator, type IDistributedTaskRequestResult } from './taskbuffer.classes.distributedcoordinator.js';
|
import {
|
||||||
|
AbstractDistributedCoordinator,
|
||||||
|
type IDistributedTaskRequestResult,
|
||||||
|
} from './taskbuffer.classes.distributedcoordinator.js';
|
||||||
|
|
||||||
export interface ICronJob {
|
export interface ICronJob {
|
||||||
cronString: string;
|
cronString: string;
|
||||||
@@ -66,7 +69,10 @@ export class TaskManager {
|
|||||||
async (triggerTime: number) => {
|
async (triggerTime: number) => {
|
||||||
this.logTaskState(task);
|
this.logTaskState(task);
|
||||||
if (this.options.distributedCoordinator) {
|
if (this.options.distributedCoordinator) {
|
||||||
const announcementResult = await this.performDistributedConsultation(task, triggerTime);
|
const announcementResult = await this.performDistributedConsultation(
|
||||||
|
task,
|
||||||
|
triggerTime,
|
||||||
|
);
|
||||||
if (!announcementResult.shouldTrigger) {
|
if (!announcementResult.shouldTrigger) {
|
||||||
console.log('Distributed coordinator result: NOT EXECUTING');
|
console.log('Distributed coordinator result: NOT EXECUTING');
|
||||||
return;
|
return;
|
||||||
@@ -75,7 +81,7 @@ export class TaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await task.trigger();
|
await task.trigger();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
task.cronJob = cronJob;
|
task.cronJob = cronJob;
|
||||||
}
|
}
|
||||||
@@ -88,7 +94,10 @@ export class TaskManager {
|
|||||||
console.log(`Task >>${task.name}<< is ${bufferState}`);
|
console.log(`Task >>${task.name}<< is ${bufferState}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async performDistributedConsultation(task: Task, triggerTime: number): Promise<IDistributedTaskRequestResult> {
|
private async performDistributedConsultation(
|
||||||
|
task: Task,
|
||||||
|
triggerTime: number,
|
||||||
|
): Promise<IDistributedTaskRequestResult> {
|
||||||
console.log('Found a distributed coordinator, performing consultation.');
|
console.log('Found a distributed coordinator, performing consultation.');
|
||||||
|
|
||||||
return this.options.distributedCoordinator.fireDistributedTaskRequest({
|
return this.options.distributedCoordinator.fireDistributedTaskRequest({
|
||||||
|
@@ -5,7 +5,8 @@ import { Task } from './taskbuffer.classes.task.js';
|
|||||||
export class TaskRunner {
|
export class TaskRunner {
|
||||||
public maxParrallelJobs: number = 1;
|
public maxParrallelJobs: number = 1;
|
||||||
public status: 'stopped' | 'running' = 'stopped';
|
public status: 'stopped' | 'running' = 'stopped';
|
||||||
public runningTasks: plugins.lik.ObjectMap<Task> = new plugins.lik.ObjectMap<Task>();
|
public runningTasks: plugins.lik.ObjectMap<Task> =
|
||||||
|
new plugins.lik.ObjectMap<Task>();
|
||||||
public qeuedTasks: Task[] = [];
|
public qeuedTasks: Task[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@@ -6,4 +6,12 @@ import * as smartrx from '@push.rocks/smartrx';
|
|||||||
import * as smarttime from '@push.rocks/smarttime';
|
import * as smarttime from '@push.rocks/smarttime';
|
||||||
import * as smartunique from '@push.rocks/smartunique';
|
import * as smartunique from '@push.rocks/smartunique';
|
||||||
|
|
||||||
export { lik, smartlog, smartpromise, smartdelay, smartrx, smarttime, smartunique };
|
export {
|
||||||
|
lik,
|
||||||
|
smartlog,
|
||||||
|
smartpromise,
|
||||||
|
smartdelay,
|
||||||
|
smartrx,
|
||||||
|
smarttime,
|
||||||
|
smartunique,
|
||||||
|
};
|
||||||
|
@@ -6,9 +6,9 @@
|
|||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"verbatimModuleSyntax": true
|
"verbatimModuleSyntax": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {}
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": ["dist_*/**/*.d.ts"]
|
||||||
"dist_*/**/*.d.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user