BREAKING CHANGE(taskbuffer): Introduce constraint-based concurrency with TaskConstraintGroup and TaskManager integration; remove legacy TaskRunner and several Task APIs (breaking); add typed Task.data and update exports and tests.
This commit is contained in:
@@ -19,18 +19,18 @@ export type TPreOrAfterTaskFunction = () => Task<any>;
|
||||
// Type helper to extract step names from array
|
||||
export type StepNames<T> = T extends ReadonlyArray<{ name: infer N }> ? N : never;
|
||||
|
||||
export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []> {
|
||||
export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = [], TData extends Record<string, unknown> = Record<string, unknown>> {
|
||||
public static extractTask<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
||||
preOrAfterTaskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
||||
): Task<T, TSteps> {
|
||||
preOrAfterTaskArg: Task<T, TSteps, any> | TPreOrAfterTaskFunction,
|
||||
): Task<T, TSteps, any> {
|
||||
switch (true) {
|
||||
case !preOrAfterTaskArg:
|
||||
return null;
|
||||
case preOrAfterTaskArg instanceof Task:
|
||||
return preOrAfterTaskArg as Task<T, TSteps>;
|
||||
return preOrAfterTaskArg as Task<T, TSteps, any>;
|
||||
case typeof preOrAfterTaskArg === 'function':
|
||||
const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
|
||||
return taskFunction() as unknown as Task<T, TSteps>;
|
||||
return taskFunction() as unknown as Task<T, TSteps, any>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
return done.promise;
|
||||
};
|
||||
|
||||
public static isTask = (taskArg: Task<any>): boolean => {
|
||||
public static isTask = (taskArg: Task<any, any, any>): boolean => {
|
||||
if (taskArg instanceof Task && typeof taskArg.taskFunction === 'function') {
|
||||
return true;
|
||||
} else {
|
||||
@@ -51,8 +51,8 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
};
|
||||
|
||||
public static isTaskTouched<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
||||
taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
||||
touchedTasksArray: Task<T, TSteps>[],
|
||||
taskArg: Task<T, TSteps, any> | TPreOrAfterTaskFunction,
|
||||
touchedTasksArray: Task<T, TSteps, any>[],
|
||||
): boolean {
|
||||
const taskToCheck = Task.extractTask(taskArg);
|
||||
let result = false;
|
||||
@@ -65,25 +65,16 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
}
|
||||
|
||||
public static runTask = async <T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
||||
taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
||||
optionsArg: { x?: any; touchedTasksArray?: Task<T, TSteps>[] },
|
||||
taskArg: Task<T, TSteps, any> | TPreOrAfterTaskFunction,
|
||||
optionsArg: { x?: any; touchedTasksArray?: Task<T, TSteps, any>[] },
|
||||
) => {
|
||||
const taskToRun = Task.extractTask(taskArg);
|
||||
const done = plugins.smartpromise.defer();
|
||||
|
||||
// Wait for all blocking tasks to finish
|
||||
for (const task of taskToRun.blockingTasks) {
|
||||
await task.finished;
|
||||
}
|
||||
|
||||
if (!taskToRun.setupValue && taskToRun.taskSetup) {
|
||||
taskToRun.setupValue = await taskToRun.taskSetup();
|
||||
}
|
||||
|
||||
if (taskToRun.execDelay) {
|
||||
await plugins.smartdelay.delayFor(taskToRun.execDelay);
|
||||
}
|
||||
|
||||
taskToRun.running = true;
|
||||
taskToRun.runCount++;
|
||||
taskToRun.lastRun = new Date();
|
||||
@@ -100,26 +91,10 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
// Complete all steps when task finishes
|
||||
taskToRun.completeAllSteps();
|
||||
taskToRun.emitEvent(taskToRun.lastError ? 'failed' : 'completed');
|
||||
|
||||
// When the task has finished running, resolve the finished promise
|
||||
taskToRun.resolveFinished();
|
||||
|
||||
// Create a new finished promise for the next run
|
||||
taskToRun.finished = new Promise((resolve) => {
|
||||
taskToRun.resolveFinished = resolve;
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
taskToRun.running = false;
|
||||
taskToRun.emitEvent('failed', { error: err instanceof Error ? err.message : String(err) });
|
||||
|
||||
// Resolve finished so blocking dependants don't hang
|
||||
taskToRun.resolveFinished();
|
||||
|
||||
// Create a new finished promise for the next run
|
||||
taskToRun.finished = new Promise((resolve) => {
|
||||
taskToRun.resolveFinished = resolve;
|
||||
});
|
||||
});
|
||||
|
||||
const options = {
|
||||
@@ -127,7 +102,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
...optionsArg,
|
||||
};
|
||||
const x = options.x;
|
||||
const touchedTasksArray: Task<T, TSteps>[] = options.touchedTasksArray;
|
||||
const touchedTasksArray: Task<T, TSteps, any>[] = options.touchedTasksArray;
|
||||
|
||||
touchedTasksArray.push(taskToRun);
|
||||
|
||||
@@ -198,18 +173,12 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
public cronJob: plugins.smarttime.CronJob;
|
||||
|
||||
public bufferMax: number;
|
||||
public execDelay: number;
|
||||
public timeout: number;
|
||||
|
||||
public preTask: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
public afterTask: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
public data: TData;
|
||||
|
||||
// Add a list to store the blocking tasks
|
||||
public blockingTasks: Task[] = [];
|
||||
|
||||
// Add a promise that will resolve when the task has finished
|
||||
private finished: Promise<void>;
|
||||
private resolveFinished: () => void;
|
||||
public preTask: Task<T, any, any> | TPreOrAfterTaskFunction;
|
||||
public afterTask: Task<T, any, any> | TPreOrAfterTaskFunction;
|
||||
|
||||
public running: boolean = false;
|
||||
public bufferRunner = new BufferRunner(this);
|
||||
@@ -275,12 +244,12 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
|
||||
constructor(optionsArg: {
|
||||
taskFunction: ITaskFunction<T>;
|
||||
preTask?: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
afterTask?: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
preTask?: Task<T, any, any> | TPreOrAfterTaskFunction;
|
||||
afterTask?: Task<T, any, any> | TPreOrAfterTaskFunction;
|
||||
buffered?: boolean;
|
||||
bufferMax?: number;
|
||||
execDelay?: number;
|
||||
name?: string;
|
||||
data?: TData;
|
||||
taskSetup?: ITaskSetupFunction<T>;
|
||||
steps?: TSteps;
|
||||
catchErrors?: boolean;
|
||||
@@ -291,8 +260,8 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
this.afterTask = optionsArg.afterTask;
|
||||
this.buffered = optionsArg.buffered;
|
||||
this.bufferMax = optionsArg.bufferMax;
|
||||
this.execDelay = optionsArg.execDelay;
|
||||
this.name = optionsArg.name;
|
||||
this.data = optionsArg.data ?? ({} as TData);
|
||||
this.taskSetup = optionsArg.taskSetup;
|
||||
this.catchErrors = optionsArg.catchErrors ?? false;
|
||||
this.labels = optionsArg.labels ? { ...optionsArg.labels } : {};
|
||||
@@ -309,11 +278,6 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
|
||||
this.steps.set(stepConfig.name, step);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the finished promise
|
||||
this.finished = new Promise((resolve) => {
|
||||
this.resolveFinished = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
public trigger(x?: any): Promise<any> {
|
||||
|
||||
Reference in New Issue
Block a user