Skip to content

Instantly share code, notes, and snippets.

@Wicpar
Created May 22, 2019 15:16
Show Gist options
  • Save Wicpar/89ac7fb62dae12a76c475d62408630bf to your computer and use it in GitHub Desktop.
Save Wicpar/89ac7fb62dae12a76c475d62408630bf to your computer and use it in GitHub Desktop.
A Typescript task manager that allows promises to be run sequencially. Can be paused and resumed. An onStart event is added to the enqueued promise.
function skipOne(): Promise<void> {
return new Promise(((resolve, reject) => {
setTimeout(resolve, 0)
}))
}
export class TaskManagerPromise<T> extends Promise<T> {
private started: boolean = false;
private startQueue: Array<() => void> = [];
private start() {
if (!this.started) {
this.started = true;
this.startQueue.forEach(async (it) => {
await skipOne();
it()
});
delete this.startQueue;
}
}
private static async computeStart<TResult = void>(onstart: (() => TResult | PromiseLike<TResult>) | TResult): Promise<TResult> {
if (onstart instanceof Function) {
return onstart()
} else {
return onstart
}
}
async onStart<TResult>(onstart: (() => TResult | PromiseLike<TResult>) | TResult): Promise<T> {
if (this.started) {
await TaskManagerPromise.computeStart(onstart);
return this
} else {
await new Promise<TResult>((resolve, reject) => {
this.startQueue.push(async () => {
resolve(await TaskManagerPromise.computeStart(onstart));
})
});
return this
}
}
constructor(executor: (start: () => void,resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
super((resolve, reject) => {
executor(()=>{this.start()}, resolve, reject)
});
}
}
export class TaskManager {
private running: boolean;
private activated: boolean = false;
private queue: Array<()=>Promise<any>> = [];
private readonly onDeactivate: () => void;
private readonly onActivate: () => void;
constructor(running: boolean = true, onActivate?: ()=>void, onDeactivate?: ()=>void) {
this.running = running;
this.onDeactivate = onDeactivate || (()=>{return});
this.onActivate = onActivate || (()=>{return});
if (running) {
this.activate()
}
}
isRunning(): boolean {
return this.activated
}
pause() {
this.running = false
}
resume() {
this.running = true;
this.activate()
}
private async activate(): Promise<void> {
if (!this.activated) {
this.activated = true;
await skipOne();
this.onActivate();
while (this.queue.length > 0 && this.running) {
await skipOne();
const current = this.queue.shift();
if (current) {
await current()
} else {
break
}
}
await skipOne();
this.activated = false;
this.onDeactivate();
}
}
enqueue<TResult>(executor: (() => TResult | PromiseLike<TResult>) | TResult): TaskManagerPromise<TResult> {
const promise = new TaskManagerPromise<TResult>((async (start, resolve, reject) => {
this.queue.push(async ()=>{
try {
await start();
let result;
if (executor instanceof Function) {
result = await executor()
} else {
result = executor
}
await resolve(result)
} catch (e) {
await reject(e)
}
})
}));
if (this.running) {
this.activate()
}
return promise
}
}
export default TaskManager;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment