2022-03-25 11:14:49 +00:00
|
|
|
import * as plugins from './taskbuffer.plugins.js';
|
|
|
|
import { BufferRunner } from './taskbuffer.classes.bufferrunner.js';
|
|
|
|
import { CycleCounter } from './taskbuffer.classes.cyclecounter.js';
|
2016-05-04 01:44:54 +00:00
|
|
|
|
2022-03-25 11:14:49 +00:00
|
|
|
import { logger } from './taskbuffer.logging.js';
|
2020-07-12 00:48:51 +00:00
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
export interface ITaskFunction<T = undefined> {
|
|
|
|
(x?: any, setupValue?: T): PromiseLike<any>;
|
2016-08-01 11:17:15 +00:00
|
|
|
}
|
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
export interface ITaskSetupFunction<T = undefined> {
|
|
|
|
(): Promise<T>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export type TPreOrAfterTaskFunction = () => Task<any>;
|
2019-09-23 16:06:43 +00:00
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
export class Task<T = undefined> {
|
2023-08-04 11:03:28 +00:00
|
|
|
public static extractTask<T = undefined>(
|
|
|
|
preOrAfterTaskArg: Task<T> | TPreOrAfterTaskFunction
|
|
|
|
): Task<T> {
|
2019-11-28 11:33:26 +00:00
|
|
|
switch (true) {
|
|
|
|
case !preOrAfterTaskArg:
|
2019-09-23 16:06:43 +00:00
|
|
|
return null;
|
2019-11-28 11:33:26 +00:00
|
|
|
case preOrAfterTaskArg instanceof Task:
|
2023-08-01 22:51:43 +00:00
|
|
|
return preOrAfterTaskArg as Task<T>;
|
2019-11-28 11:33:26 +00:00
|
|
|
case typeof preOrAfterTaskArg === 'function':
|
2019-09-23 16:06:43 +00:00
|
|
|
const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
|
|
|
|
return taskFunction();
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-12 00:48:51 +00:00
|
|
|
public static emptyTaskFunction: ITaskFunction = function (x) {
|
2019-09-23 16:06:43 +00:00
|
|
|
const done = plugins.smartpromise.defer();
|
|
|
|
done.resolve();
|
|
|
|
return done.promise;
|
|
|
|
};
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
public static isTask = (taskArg: Task<any>): boolean => {
|
2019-09-23 16:06:43 +00:00
|
|
|
if (taskArg instanceof Task && typeof taskArg.taskFunction === 'function') {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2023-08-04 11:03:28 +00:00
|
|
|
public static isTaskTouched<T = undefined>(
|
2023-08-01 22:51:43 +00:00
|
|
|
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
|
|
|
touchedTasksArray: Task<T>[]
|
|
|
|
): boolean {
|
2019-09-23 16:06:43 +00:00
|
|
|
const taskToCheck = Task.extractTask(taskArg);
|
|
|
|
let result = false;
|
|
|
|
for (const keyArg in touchedTasksArray) {
|
|
|
|
if (taskToCheck === touchedTasksArray[keyArg]) {
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2023-08-04 11:03:28 +00:00
|
|
|
}
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
public static runTask = async <T>(
|
|
|
|
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
|
|
|
optionsArg: { x?: any; touchedTasksArray?: Task<T>[] }
|
2019-11-28 11:33:26 +00:00
|
|
|
) => {
|
2019-09-23 16:06:43 +00:00
|
|
|
const taskToRun = Task.extractTask(taskArg);
|
|
|
|
const done = plugins.smartpromise.defer();
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2023-08-04 11:03:28 +00:00
|
|
|
// Wait for all blocking tasks to finish
|
|
|
|
for (const task of taskToRun.blockingTasks) {
|
|
|
|
await task.finished;
|
|
|
|
}
|
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
if (!taskToRun.setupValue && taskToRun.taskSetup) {
|
|
|
|
taskToRun.setupValue = await taskToRun.taskSetup();
|
|
|
|
}
|
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
if (taskToRun.execDelay) {
|
|
|
|
await plugins.smartdelay.delayFor(taskToRun.execDelay);
|
|
|
|
}
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
taskToRun.running = true;
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
done.promise.then(async () => {
|
|
|
|
taskToRun.running = false;
|
2023-08-04 11:03:28 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
});
|
2019-09-23 16:06:43 +00:00
|
|
|
});
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
const options = {
|
|
|
|
...{ x: undefined, touchedTasksArray: [] },
|
2020-07-12 00:48:51 +00:00
|
|
|
...optionsArg,
|
2019-09-23 16:06:43 +00:00
|
|
|
};
|
|
|
|
const x = options.x;
|
2023-08-01 22:51:43 +00:00
|
|
|
const touchedTasksArray: Task<T>[] = options.touchedTasksArray;
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
touchedTasksArray.push(taskToRun);
|
2019-11-28 11:33:26 +00:00
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
const localDeferred = plugins.smartpromise.defer();
|
|
|
|
localDeferred.promise
|
|
|
|
.then(() => {
|
|
|
|
if (taskToRun.preTask && !Task.isTaskTouched(taskToRun.preTask, touchedTasksArray)) {
|
|
|
|
return Task.runTask(taskToRun.preTask, { x, touchedTasksArray });
|
|
|
|
} else {
|
|
|
|
const done2 = plugins.smartpromise.defer();
|
|
|
|
done2.resolve(x);
|
|
|
|
return done2.promise;
|
|
|
|
}
|
|
|
|
})
|
2020-07-12 00:48:51 +00:00
|
|
|
.then(async (x) => {
|
2019-09-23 16:06:43 +00:00
|
|
|
try {
|
2023-08-01 22:51:43 +00:00
|
|
|
return await taskToRun.taskFunction(x, taskToRun.setupValue);
|
2019-09-23 16:06:43 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
}
|
|
|
|
})
|
2020-07-12 00:48:51 +00:00
|
|
|
.then((x) => {
|
2019-09-23 16:06:43 +00:00
|
|
|
if (taskToRun.afterTask && !Task.isTaskTouched(taskToRun.afterTask, touchedTasksArray)) {
|
|
|
|
return Task.runTask(taskToRun.afterTask, { x: x, touchedTasksArray: touchedTasksArray });
|
|
|
|
} else {
|
|
|
|
const done2 = plugins.smartpromise.defer();
|
|
|
|
done2.resolve(x);
|
|
|
|
return done2.promise;
|
|
|
|
}
|
|
|
|
})
|
2020-07-12 00:48:51 +00:00
|
|
|
.then((x) => {
|
2019-09-23 16:06:43 +00:00
|
|
|
done.resolve(x);
|
|
|
|
})
|
2020-07-12 00:48:51 +00:00
|
|
|
.catch((err) => {
|
2019-09-23 16:06:43 +00:00
|
|
|
console.log(err);
|
|
|
|
});
|
|
|
|
localDeferred.resolve();
|
|
|
|
return await done.promise;
|
2019-11-28 11:33:26 +00:00
|
|
|
};
|
2019-09-23 16:06:43 +00:00
|
|
|
|
|
|
|
public name: string;
|
2022-11-14 13:54:26 +00:00
|
|
|
public version: string;
|
2023-08-01 22:51:43 +00:00
|
|
|
public taskFunction: ITaskFunction<T>;
|
2019-09-23 16:06:43 +00:00
|
|
|
public buffered: boolean;
|
2020-07-12 00:48:51 +00:00
|
|
|
public cronJob: plugins.smarttime.CronJob;
|
2017-07-10 10:42:06 +00:00
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
public bufferMax: number;
|
|
|
|
public execDelay: number;
|
2022-11-14 13:54:26 +00:00
|
|
|
public timeout: number;
|
2017-07-10 10:42:06 +00:00
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
public preTask: Task<T> | TPreOrAfterTaskFunction;
|
|
|
|
public afterTask: Task<T> | TPreOrAfterTaskFunction;
|
2016-05-04 01:44:54 +00:00
|
|
|
|
2023-08-04 11:03:28 +00:00
|
|
|
// 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;
|
|
|
|
|
2019-09-23 16:06:43 +00:00
|
|
|
public running: boolean = false;
|
|
|
|
public bufferRunner = new BufferRunner(this);
|
|
|
|
public cycleCounter = new CycleCounter(this);
|
2017-07-10 10:42:06 +00:00
|
|
|
|
2023-08-04 11:03:28 +00:00
|
|
|
public get idle() {
|
|
|
|
return !this.running;
|
|
|
|
}
|
2016-08-01 14:10:00 +00:00
|
|
|
|
2023-08-01 22:51:43 +00:00
|
|
|
public taskSetup: ITaskSetupFunction<T>;
|
|
|
|
public setupValue: T;
|
|
|
|
|
2017-07-10 10:42:06 +00:00
|
|
|
constructor(optionsArg: {
|
2023-08-01 22:51:43 +00:00
|
|
|
taskFunction: ITaskFunction<T>;
|
|
|
|
preTask?: Task<T> | TPreOrAfterTaskFunction;
|
|
|
|
afterTask?: Task<T> | TPreOrAfterTaskFunction;
|
2018-08-04 15:53:22 +00:00
|
|
|
buffered?: boolean;
|
|
|
|
bufferMax?: number;
|
|
|
|
execDelay?: number;
|
|
|
|
name?: string;
|
2023-08-01 22:51:43 +00:00
|
|
|
taskSetup?: ITaskSetupFunction<T>;
|
2017-02-15 21:52:29 +00:00
|
|
|
}) {
|
2018-08-04 15:53:22 +00:00
|
|
|
this.taskFunction = optionsArg.taskFunction;
|
|
|
|
this.preTask = optionsArg.preTask;
|
|
|
|
this.afterTask = optionsArg.afterTask;
|
|
|
|
this.buffered = optionsArg.buffered;
|
|
|
|
this.bufferMax = optionsArg.bufferMax;
|
|
|
|
this.execDelay = optionsArg.execDelay;
|
|
|
|
this.name = optionsArg.name;
|
2023-08-01 22:51:43 +00:00
|
|
|
this.taskSetup = optionsArg.taskSetup;
|
2023-08-04 11:03:28 +00:00
|
|
|
|
|
|
|
// Create the finished promise
|
|
|
|
this.finished = new Promise((resolve) => {
|
|
|
|
this.resolveFinished = resolve;
|
|
|
|
});
|
2017-02-15 21:52:29 +00:00
|
|
|
}
|
|
|
|
|
2021-09-26 12:45:02 +00:00
|
|
|
public trigger(x?: any): Promise<any> {
|
2017-02-15 21:52:29 +00:00
|
|
|
if (this.buffered) {
|
2018-08-04 15:53:22 +00:00
|
|
|
return this.triggerBuffered(x);
|
2017-06-09 21:33:41 +00:00
|
|
|
} else {
|
2018-08-04 15:53:22 +00:00
|
|
|
return this.triggerUnBuffered(x);
|
2017-06-09 21:33:41 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-01 11:17:15 +00:00
|
|
|
|
2021-09-26 12:45:02 +00:00
|
|
|
public triggerUnBuffered(x?: any): Promise<any> {
|
2023-08-01 22:51:43 +00:00
|
|
|
return Task.runTask<T>(this, { x: x });
|
2017-02-15 21:52:29 +00:00
|
|
|
}
|
2016-05-04 01:44:54 +00:00
|
|
|
|
2021-09-26 12:45:02 +00:00
|
|
|
public triggerBuffered(x?: any): Promise<any> {
|
2018-08-04 15:53:22 +00:00
|
|
|
return this.bufferRunner.trigger(x);
|
2017-02-15 21:52:29 +00:00
|
|
|
}
|
2017-06-09 21:33:41 +00:00
|
|
|
}
|