fix(core): update
This commit is contained in:
parent
fa8be6b6d3
commit
5240a80cb3
@ -1,116 +1,96 @@
|
|||||||
import { expect, tap } from '@push.rocks/tapbundle';
|
import { expect, tap } from '@push.rocks/tapbundle';
|
||||||
|
|
||||||
import * as taskbuffer from '../ts/index.js';
|
import * as taskbuffer from '../ts/index.js';
|
||||||
|
|
||||||
import * as smartpromise from '@push.rocks/smartpromise';
|
import * as smartpromise from '@push.rocks/smartpromise';
|
||||||
import * as smartdelay from '@push.rocks/smartdelay';
|
import * as smartdelay from '@push.rocks/smartdelay';
|
||||||
|
|
||||||
// setup some testData to work with
|
|
||||||
let testTask: taskbuffer.Task;
|
|
||||||
|
|
||||||
let testPreTask = new taskbuffer.Task({
|
|
||||||
taskFunction: async () => {
|
|
||||||
console.log('preTask executed');
|
|
||||||
},
|
|
||||||
preTask: testTask,
|
|
||||||
});
|
|
||||||
|
|
||||||
// some more tasks to test with
|
|
||||||
let task1Counter = 0; // how often task 1 is being executed
|
|
||||||
let task1 = new taskbuffer.Task({
|
|
||||||
name: 'Task 1',
|
|
||||||
taskFunction: () => {
|
|
||||||
let done = smartpromise.defer();
|
|
||||||
console.log('Task1 started');
|
|
||||||
setTimeout(() => {
|
|
||||||
task1Counter++;
|
|
||||||
console.log('Task1 executed');
|
|
||||||
done.resolve();
|
|
||||||
}, 5000);
|
|
||||||
return done.promise;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let task2 = new taskbuffer.Task({
|
|
||||||
name: 'Task 1',
|
|
||||||
taskFunction: async () => {
|
|
||||||
const done = smartpromise.defer();
|
|
||||||
console.log('Task2 started');
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log('Task2 executed');
|
|
||||||
done.resolve();
|
|
||||||
}, 5000);
|
|
||||||
await done.promise;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let task3 = new taskbuffer.Task({
|
|
||||||
name: 'Task 3',
|
|
||||||
taskFunction: () => {
|
|
||||||
let done = smartpromise.defer();
|
|
||||||
console.log('Task3 started');
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log('Task3 executed');
|
|
||||||
done.resolve();
|
|
||||||
}, 5000);
|
|
||||||
return done.promise;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('new Task() should return a new task', async () => {
|
tap.test('new Task() should return a new task', async () => {
|
||||||
testTask = new taskbuffer.Task({
|
const testTask = new taskbuffer.Task({
|
||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
console.log('executed twice');
|
console.log('executed twice');
|
||||||
},
|
},
|
||||||
preTask: testPreTask,
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('expect testTask to be an instance of Task', async () => {
|
|
||||||
expect(testTask).toBeInstanceOf(taskbuffer.Task);
|
expect(testTask).toBeInstanceOf(taskbuffer.Task);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('expect testTask.idle is true', async () => {
|
tap.test('should be able to get the task state', async () => {
|
||||||
if (!testTask.idle) {
|
const testTask = new taskbuffer.Task({
|
||||||
throw new Error('testTask.idle is not true');
|
taskFunction: async () => {},
|
||||||
}
|
});
|
||||||
|
expect(testTask.state).toEqual('ready');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('testTask.running should be of type boolean and initially false', async () => {
|
tap.test('should have bufferMax set to the provided value', async () => {
|
||||||
expect(testTask.running).toBeTypeofBoolean();
|
const task2 = new taskbuffer.Task({
|
||||||
// tslint:disable-next-line:no-unused-expression
|
taskFunction: async () => {},
|
||||||
expect(testTask.running).toBeFalse();
|
});
|
||||||
|
expect(task2.bufferMax).toBeUndefined(); // test for a task without bufferMax set
|
||||||
|
const bufferedTask = new taskbuffer.Task({
|
||||||
|
taskFunction: async () => {},
|
||||||
|
buffered: true,
|
||||||
|
bufferMax: 3,
|
||||||
|
});
|
||||||
|
expect(bufferedTask.bufferMax).toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('testTask.trigger() should return Promise', async () => {
|
tap.test('should be able to trigger tasks multiple times', async () => {
|
||||||
expect(testTask.trigger()).toBeInstanceOf(Promise);
|
let task1Counter = 0;
|
||||||
});
|
const task1 = new taskbuffer.Task({
|
||||||
|
name: 'Task 1',
|
||||||
tap.test('testTask.trigger() returned Promise should be fullfilled', async () => {
|
taskFunction: () => {
|
||||||
await testTask.trigger();
|
let done = smartpromise.defer();
|
||||||
});
|
console.log('Task1 started');
|
||||||
|
setTimeout(() => {
|
||||||
tap.test('expect to run a task without pre and afterTask errorless', async () => {
|
task1Counter++;
|
||||||
let localTestTask = new taskbuffer.Task({
|
console.log('Task1 executed');
|
||||||
taskFunction: async () => {
|
done.resolve();
|
||||||
console.log('only once');
|
}, 5000);
|
||||||
|
return done.promise;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await localTestTask.trigger();
|
await task1.trigger();
|
||||||
|
await task1.trigger();
|
||||||
|
expect(task1Counter).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('expect task to run in buffered mode', async () => {
|
tap.test('should execute setup function before the task function', async () => {
|
||||||
let localTestTask = new taskbuffer.Task({
|
const task2 = new taskbuffer.Task({
|
||||||
|
name: 'Task 2',
|
||||||
|
taskSetup: async () => {
|
||||||
|
console.log('this is the setup function for task 2. It should only run once.')
|
||||||
|
return {
|
||||||
|
nice: 'yes',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
taskFunction: async (before, setupArg) => {
|
||||||
|
expect(setupArg).toEqual({ nice: 'yes' });
|
||||||
|
const done = smartpromise.defer();
|
||||||
|
console.log('Task2 started');
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('Task2 executed');
|
||||||
|
done.resolve();
|
||||||
|
}, 5000);
|
||||||
|
await done.promise;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await task2.trigger();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should not exceed bufferMax when task is buffered', async () => {
|
||||||
|
let counter = 0;
|
||||||
|
const bufferedTask = new taskbuffer.Task({
|
||||||
taskFunction: async () => {
|
taskFunction: async () => {
|
||||||
await smartdelay.delayFor(3000);
|
counter++;
|
||||||
|
await smartdelay.delayFor(2000);
|
||||||
|
counter--;
|
||||||
},
|
},
|
||||||
buffered: true,
|
buffered: true,
|
||||||
bufferMax: 2,
|
bufferMax: 2,
|
||||||
});
|
});
|
||||||
localTestTask.trigger();
|
bufferedTask.trigger();
|
||||||
localTestTask.trigger();
|
bufferedTask.trigger();
|
||||||
localTestTask.trigger();
|
bufferedTask.trigger();
|
||||||
await localTestTask.trigger();
|
await smartdelay.delayFor(100);
|
||||||
|
expect(counter <= bufferedTask.bufferMax).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.start();
|
tap.start();
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/taskbuffer',
|
name: '@push.rocks/taskbuffer',
|
||||||
version: '3.0.12',
|
version: '3.0.13',
|
||||||
description: 'flexible task management. TypeScript ready!'
|
description: 'flexible task management. TypeScript ready!'
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,24 @@ import { CycleCounter } from './taskbuffer.classes.cyclecounter.js';
|
|||||||
|
|
||||||
import { logger } from './taskbuffer.logging.js';
|
import { logger } from './taskbuffer.logging.js';
|
||||||
|
|
||||||
export interface ITaskFunction {
|
export interface ITaskFunction<T = undefined> {
|
||||||
(x?: any): PromiseLike<any>;
|
(x?: any, setupValue?: T): PromiseLike<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TPreOrAfterTaskFunction = () => Task;
|
export interface ITaskSetupFunction<T = undefined> {
|
||||||
|
(): Promise<T>;
|
||||||
|
}
|
||||||
|
|
||||||
export class Task {
|
export type TPreOrAfterTaskFunction = () => Task<any>;
|
||||||
|
|
||||||
|
export class Task<T = undefined> {
|
||||||
// STATIC
|
// STATIC
|
||||||
public static extractTask(preOrAfterTaskArg: Task | TPreOrAfterTaskFunction): Task {
|
public static extractTask<T = undefined>(preOrAfterTaskArg: Task<T> | TPreOrAfterTaskFunction): Task<T> {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case !preOrAfterTaskArg:
|
case !preOrAfterTaskArg:
|
||||||
return null;
|
return null;
|
||||||
case preOrAfterTaskArg instanceof Task:
|
case preOrAfterTaskArg instanceof Task:
|
||||||
return preOrAfterTaskArg as Task;
|
return preOrAfterTaskArg as Task<T>;
|
||||||
case typeof preOrAfterTaskArg === 'function':
|
case typeof preOrAfterTaskArg === 'function':
|
||||||
const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
|
const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
|
||||||
return taskFunction();
|
return taskFunction();
|
||||||
@ -32,7 +36,7 @@ export class Task {
|
|||||||
return done.promise;
|
return done.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
public static isTask = (taskArg: Task): boolean => {
|
public static isTask = (taskArg: Task<any>): boolean => {
|
||||||
if (taskArg instanceof Task && typeof taskArg.taskFunction === 'function') {
|
if (taskArg instanceof Task && typeof taskArg.taskFunction === 'function') {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -40,10 +44,10 @@ export class Task {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static isTaskTouched = (
|
public static isTaskTouched<T = undefined> (
|
||||||
taskArg: Task | TPreOrAfterTaskFunction,
|
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||||
touchedTasksArray: Task[]
|
touchedTasksArray: Task<T>[]
|
||||||
): boolean => {
|
): boolean {
|
||||||
const taskToCheck = Task.extractTask(taskArg);
|
const taskToCheck = Task.extractTask(taskArg);
|
||||||
let result = false;
|
let result = false;
|
||||||
for (const keyArg in touchedTasksArray) {
|
for (const keyArg in touchedTasksArray) {
|
||||||
@ -54,42 +58,39 @@ export class Task {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
public static runTask = async (
|
public static runTask = async <T>(
|
||||||
taskArg: Task | TPreOrAfterTaskFunction,
|
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||||
optionsArg: { x?: any; touchedTasksArray?: Task[] }
|
optionsArg: { x?: any; touchedTasksArray?: Task<T>[] }
|
||||||
) => {
|
) => {
|
||||||
// extracts the task in case it is specified as a return value of a function
|
|
||||||
const taskToRun = Task.extractTask(taskArg);
|
const taskToRun = Task.extractTask(taskArg);
|
||||||
const done = plugins.smartpromise.defer();
|
const done = plugins.smartpromise.defer();
|
||||||
|
|
||||||
// pay respect to execDelay
|
if (!taskToRun.setupValue && taskToRun.taskSetup) {
|
||||||
|
taskToRun.setupValue = await taskToRun.taskSetup();
|
||||||
|
}
|
||||||
|
|
||||||
if (taskToRun.execDelay) {
|
if (taskToRun.execDelay) {
|
||||||
await plugins.smartdelay.delayFor(taskToRun.execDelay);
|
await plugins.smartdelay.delayFor(taskToRun.execDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set running params
|
|
||||||
taskToRun.running = true;
|
taskToRun.running = true;
|
||||||
|
|
||||||
done.promise.then(async () => {
|
done.promise.then(async () => {
|
||||||
taskToRun.running = false;
|
taskToRun.running = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle options
|
|
||||||
const options = {
|
const options = {
|
||||||
...{ x: undefined, touchedTasksArray: [] },
|
...{ x: undefined, touchedTasksArray: [] },
|
||||||
...optionsArg,
|
...optionsArg,
|
||||||
};
|
};
|
||||||
const x = options.x;
|
const x = options.x;
|
||||||
const touchedTasksArray: Task[] = options.touchedTasksArray;
|
const touchedTasksArray: Task<T>[] = options.touchedTasksArray;
|
||||||
|
|
||||||
touchedTasksArray.push(taskToRun);
|
touchedTasksArray.push(taskToRun);
|
||||||
|
|
||||||
// run the task cascade
|
|
||||||
const localDeferred = plugins.smartpromise.defer();
|
const localDeferred = plugins.smartpromise.defer();
|
||||||
localDeferred.promise
|
localDeferred.promise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// lets run any preTask
|
|
||||||
|
|
||||||
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 {
|
||||||
@ -99,9 +100,8 @@ export class Task {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(async (x) => {
|
.then(async (x) => {
|
||||||
// lets run the main task
|
|
||||||
try {
|
try {
|
||||||
return await taskToRun.taskFunction(x);
|
return await taskToRun.taskFunction(x, taskToRun.setupValue);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
@ -126,15 +126,9 @@ export class Task {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// INSTANCE
|
// INSTANCE
|
||||||
// mandatory properties
|
|
||||||
public name: string;
|
public name: string;
|
||||||
/**
|
|
||||||
* the version of the task
|
|
||||||
* should follow semver
|
|
||||||
* might be important for DistributedCoordinator
|
|
||||||
*/
|
|
||||||
public version: string;
|
public version: string;
|
||||||
public taskFunction: ITaskFunction;
|
public taskFunction: ITaskFunction<T>;
|
||||||
public buffered: boolean;
|
public buffered: boolean;
|
||||||
public cronJob: plugins.smarttime.CronJob;
|
public cronJob: plugins.smarttime.CronJob;
|
||||||
|
|
||||||
@ -142,11 +136,9 @@ export class Task {
|
|||||||
public execDelay: number;
|
public execDelay: number;
|
||||||
public timeout: number;
|
public timeout: number;
|
||||||
|
|
||||||
// tasks to run before and after
|
public preTask: Task<T> | TPreOrAfterTaskFunction;
|
||||||
public preTask: Task | TPreOrAfterTaskFunction;
|
public afterTask: Task<T> | TPreOrAfterTaskFunction;
|
||||||
public afterTask: Task | TPreOrAfterTaskFunction;
|
|
||||||
|
|
||||||
// initialize by default
|
|
||||||
public running: boolean = false;
|
public running: boolean = false;
|
||||||
public bufferRunner = new BufferRunner(this);
|
public bufferRunner = new BufferRunner(this);
|
||||||
public cycleCounter = new CycleCounter(this);
|
public cycleCounter = new CycleCounter(this);
|
||||||
@ -154,36 +146,18 @@ export class Task {
|
|||||||
public idle: boolean = true;
|
public idle: boolean = true;
|
||||||
private _state: string = 'ready';
|
private _state: string = 'ready';
|
||||||
|
|
||||||
|
public taskSetup: ITaskSetupFunction<T>;
|
||||||
|
public setupValue: T;
|
||||||
|
|
||||||
constructor(optionsArg: {
|
constructor(optionsArg: {
|
||||||
/**
|
taskFunction: ITaskFunction<T>;
|
||||||
* the task function to run, must return promise
|
preTask?: Task<T> | TPreOrAfterTaskFunction;
|
||||||
*/
|
afterTask?: Task<T> | TPreOrAfterTaskFunction;
|
||||||
taskFunction: ITaskFunction;
|
|
||||||
/**
|
|
||||||
* any other task to run before
|
|
||||||
*/
|
|
||||||
preTask?: Task | TPreOrAfterTaskFunction;
|
|
||||||
/**
|
|
||||||
* any other task to run after
|
|
||||||
*/
|
|
||||||
afterTask?: Task | TPreOrAfterTaskFunction;
|
|
||||||
/**
|
|
||||||
* wether this task should run buffered
|
|
||||||
*/
|
|
||||||
buffered?: boolean;
|
buffered?: boolean;
|
||||||
/**
|
|
||||||
* the maximum buffer
|
|
||||||
*/
|
|
||||||
bufferMax?: number;
|
bufferMax?: number;
|
||||||
/**
|
|
||||||
* the execution delay, before the task is executed
|
|
||||||
* only makes sense when running in buffered mode
|
|
||||||
*/
|
|
||||||
execDelay?: number;
|
execDelay?: number;
|
||||||
/**
|
|
||||||
* the name of the task
|
|
||||||
*/
|
|
||||||
name?: string;
|
name?: string;
|
||||||
|
taskSetup?: ITaskSetupFunction<T>;
|
||||||
}) {
|
}) {
|
||||||
this.taskFunction = optionsArg.taskFunction;
|
this.taskFunction = optionsArg.taskFunction;
|
||||||
this.preTask = optionsArg.preTask;
|
this.preTask = optionsArg.preTask;
|
||||||
@ -193,11 +167,9 @@ export class Task {
|
|||||||
this.bufferMax = optionsArg.bufferMax;
|
this.bufferMax = optionsArg.bufferMax;
|
||||||
this.execDelay = optionsArg.execDelay;
|
this.execDelay = optionsArg.execDelay;
|
||||||
this.name = optionsArg.name;
|
this.name = optionsArg.name;
|
||||||
|
this.taskSetup = optionsArg.taskSetup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* trigger the task. Will trigger buffered if this.buffered is true
|
|
||||||
*/
|
|
||||||
public trigger(x?: any): Promise<any> {
|
public trigger(x?: any): Promise<any> {
|
||||||
if (this.buffered) {
|
if (this.buffered) {
|
||||||
return this.triggerBuffered(x);
|
return this.triggerBuffered(x);
|
||||||
@ -206,18 +178,10 @@ export class Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* trigger task unbuffered.
|
|
||||||
* will actually run the task, not considering any buffered limits.
|
|
||||||
*/
|
|
||||||
public triggerUnBuffered(x?: any): Promise<any> {
|
public triggerUnBuffered(x?: any): Promise<any> {
|
||||||
return Task.runTask(this, { x: x });
|
return Task.runTask<T>(this, { x: x });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* trigger task buffered.
|
|
||||||
* note: .trigger() also calls this function
|
|
||||||
*/
|
|
||||||
public triggerBuffered(x?: any): Promise<any> {
|
public triggerBuffered(x?: any): Promise<any> {
|
||||||
return this.bufferRunner.trigger(x);
|
return this.bufferRunner.trigger(x);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user