import * as plugins from './taskbuffer.plugins.js'; import { BufferRunner } from './taskbuffer.classes.bufferrunner.js'; import { CycleCounter } from './taskbuffer.classes.cyclecounter.js'; import { logger } from './taskbuffer.logging.js'; export interface ITaskFunction { (x?: any, setupValue?: T): PromiseLike; } export interface ITaskSetupFunction { (): Promise; } export type TPreOrAfterTaskFunction = () => Task; export class Task { public static extractTask( preOrAfterTaskArg: Task | TPreOrAfterTaskFunction ): Task { switch (true) { case !preOrAfterTaskArg: return null; case preOrAfterTaskArg instanceof Task: return preOrAfterTaskArg as Task; case typeof preOrAfterTaskArg === 'function': const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction; return taskFunction(); default: return null; } } public static emptyTaskFunction: ITaskFunction = function (x) { const done = plugins.smartpromise.defer(); done.resolve(); return done.promise; }; public static isTask = (taskArg: Task): boolean => { if (taskArg instanceof Task && typeof taskArg.taskFunction === 'function') { return true; } else { return false; } }; public static isTaskTouched( taskArg: Task | TPreOrAfterTaskFunction, touchedTasksArray: Task[] ): boolean { const taskToCheck = Task.extractTask(taskArg); let result = false; for (const keyArg in touchedTasksArray) { if (taskToCheck === touchedTasksArray[keyArg]) { result = true; } } return result; } public static runTask = async ( taskArg: Task | TPreOrAfterTaskFunction, optionsArg: { x?: any; touchedTasksArray?: Task[] } ) => { 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; done.promise.then(async () => { taskToRun.running = false; // 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; }); }); const options = { ...{ x: undefined, touchedTasksArray: [] }, ...optionsArg, }; const x = options.x; const touchedTasksArray: Task[] = options.touchedTasksArray; touchedTasksArray.push(taskToRun); 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; } }) .then(async (x) => { try { return await taskToRun.taskFunction(x, taskToRun.setupValue); } catch (e) { console.log(e); } }) .then((x) => { 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; } }) .then((x) => { done.resolve(x); }) .catch((err) => { console.log(err); }); localDeferred.resolve(); return await done.promise; }; public name: string; public version: string; public taskFunction: ITaskFunction; public buffered: boolean; public cronJob: plugins.smarttime.CronJob; public bufferMax: number; public execDelay: number; public timeout: number; public preTask: Task | TPreOrAfterTaskFunction; public afterTask: Task | TPreOrAfterTaskFunction; // 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; private resolveFinished: () => void; public running: boolean = false; public bufferRunner = new BufferRunner(this); public cycleCounter = new CycleCounter(this); public get idle() { return !this.running; } public taskSetup: ITaskSetupFunction; public setupValue: T; constructor(optionsArg: { taskFunction: ITaskFunction; preTask?: Task | TPreOrAfterTaskFunction; afterTask?: Task | TPreOrAfterTaskFunction; buffered?: boolean; bufferMax?: number; execDelay?: number; name?: string; taskSetup?: ITaskSetupFunction; }) { 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; this.taskSetup = optionsArg.taskSetup; // Create the finished promise this.finished = new Promise((resolve) => { this.resolveFinished = resolve; }); } public trigger(x?: any): Promise { if (this.buffered) { return this.triggerBuffered(x); } else { return this.triggerUnBuffered(x); } } public triggerUnBuffered(x?: any): Promise { return Task.runTask(this, { x: x }); } public triggerBuffered(x?: any): Promise { return this.bufferRunner.trigger(x); } }