-
-
Save licaomeng/60b682db358c01e936c0af6bd9a6a70d 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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