UNPKG

@leafer/task

Version:
287 lines (212 loc) 6.83 kB
import { IFunction, ITaskProcessor, ITaskProcessorConfig, ITaskOptions, ITaskItem } from '@leafer/interface' import { DataHelper, isNumber, isUndefined } from '@leafer/data' import { TaskItem } from './TaskItem' export class TaskProcessor implements ITaskProcessor { public config: ITaskProcessorConfig = { parallel: 6 } protected list: ITaskItem[] = [] protected parallelList: ITaskItem[] protected parallelSuccessNumber: number public running = false public isComplete = true protected timer: any public get total(): number { return this.list.length + this.delayNumber } public index = 0 public delayNumber = 0 // 延迟执行任务 public get finishedIndex(): number { return this.isComplete ? 0 : this.index + this.parallelSuccessNumber } public get remain(): number { return this.isComplete ? this.total : this.total - this.finishedIndex } public get percent(): number { const { total } = this let totalTime = 0, runTime = 0 for (let i = 0; i < total; i++) { if (i <= this.finishedIndex) { runTime += this.list[i].time if (i === this.finishedIndex) totalTime = runTime } else { totalTime += this.list[i].time } } return this.isComplete ? 1 : (runTime / totalTime) } constructor(config?: ITaskProcessorConfig) { if (config) DataHelper.assign(this.config, config) this.empty() } // list public add(taskCallback: IFunction, options?: ITaskOptions | number): ITaskItem { let start: boolean, parallel: boolean, time: number, delay: number const task = new TaskItem(taskCallback) task.parent = this if (isNumber(options)) { delay = options } else if (options) { parallel = options.parallel start = options.start time = options.time delay = options.delay } if (time) task.time = time if (parallel === false) task.parallel = false if (isUndefined(delay)) { this.push(task, start) } else { this.delayNumber++ setTimeout(() => { if (this.delayNumber) { this.delayNumber-- this.push(task, start) } }, delay) } this.isComplete = false return task } protected push(task: ITaskItem, start?: boolean): void { this.list.push(task) if (start !== false && !this.timer) { this.timer = setTimeout(() => this.start()) } } protected empty(): void { this.index = 0 this.parallelSuccessNumber = 0 this.list = [] this.parallelList = [] this.delayNumber = 0 } // control public start(): void { if (!this.running) { this.running = true this.isComplete = false this.run() } } public pause(): void { clearTimeout(this.timer) this.timer = null this.running = false } public resume(): void { this.start() } public skip(): void { this.index++ this.resume() } public stop(): void { this.isComplete = true this.list.forEach(task => { if (!task.isComplete) task.cancel() }) this.pause() this.empty() } // run protected run(): void { if (!this.running) return this.setParallelList() if (this.parallelList.length > 1) { this.runParallelTasks() } else { this.remain ? this.runTask() : this.onComplete() } } protected runTask(): void { const task = this.list[this.index] if (!task) { this.nextTask() // 存在延时任务 return } task.run().then(() => { this.onTask(task) this.index++ this.nextTask() }).catch(error => { this.onError(error) }) } protected runParallelTasks(): void { this.parallelList.forEach(task => this.runParallelTask(task)) } protected runParallelTask(task: ITaskItem): void { task.run().then(() => { this.onTask(task) this.fillParallelTask() }).catch(error => { this.onParallelError(error) }) } private nextTask(): void { if (this.total === this.finishedIndex) { this.onComplete() } else { this.timer = setTimeout(() => this.run()) } } protected setParallelList(): void { let task: ITaskItem this.parallelList = [] this.parallelSuccessNumber = 0 let end = this.index + this.config.parallel if (end > this.list.length) end = this.list.length for (let i = this.index; i < end; i++) { task = this.list[i] if (task.parallel) { this.parallelList.push(task) } else { break } } } protected fillParallelTask(): void { let task: ITaskItem const parallelList = this.parallelList // 完成一个任务 this.parallelSuccessNumber++ parallelList.pop() // 找到下一个可以并行的任务 const parallelWaitNumber = parallelList.length const nextIndex = this.finishedIndex + parallelWaitNumber if (parallelList.length) { if (!this.running) return if (nextIndex < this.total) { task = this.list[nextIndex] if (task && task.parallel) { parallelList.push(task) this.runParallelTask(task) } } } else { this.index += this.parallelSuccessNumber this.parallelSuccessNumber = 0 this.nextTask() } } // event protected onComplete(): void { this.stop() if (this.config.onComplete) this.config.onComplete() } protected onTask(task: ITaskItem): void { task.complete() if (this.config.onTask) this.config.onTask() } protected onParallelError(error: unknown): void { // 并行变串行, 以便下次重试 this.parallelList.forEach(task => { task.parallel = false }) this.parallelList.length = 0 this.parallelSuccessNumber = 0 this.onError(error) } protected onError(error: unknown): void { this.pause() if (this.config.onError) this.config.onError(error) } public destroy(): void { this.stop() } }