type ResolveFn<T = string> = (value: T | PromiseLike<T>) => void
|
|
type RejectFn = (reason?: any) => void
|
|
type PerStepFn = (r: number) => void
|
/**
|
* 步进计时器
|
*
|
* @example
|
* const timer = new Timer(10_000, 1_000)
|
* timer
|
* .start((round: number) => {
|
* console.log(round)
|
* })
|
* .then(
|
* () => {
|
* console.log('over')
|
* },
|
* (err) => {
|
* console.log(err)
|
* }
|
* )
|
*/
|
class StepTimer {
|
private timer?: number
|
/** 计时器总时长 单位毫秒 */
|
private readonly duration: number = 0
|
/** 计时步长 单位毫秒 */
|
private readonly step: number = 1000
|
/** 计时轮数 */
|
private round: number = 0
|
private blockFlag: boolean = false
|
private reject?: RejectFn
|
private perStep?: PerStepFn
|
private resolve?: ResolveFn
|
|
/**
|
* 创建计时器
|
* @param duration 计时器总持续时间
|
* @param step 计时间隔
|
*/
|
constructor(duration: number, step: number) {
|
this.duration = duration
|
this.step = step
|
this.round = 0
|
}
|
|
/**
|
* 启动计时器
|
* 接受一个函数, 每轮触发一次
|
* 返回 Promise, 在计时结束后 resolve
|
* 重复调用会清除之前的计时器重新计时
|
* @param perStep
|
*/
|
start(perStep: PerStepFn): Promise<string> {
|
if (this.timer) {
|
this.reset()
|
}
|
this.perStep = perStep
|
return new Promise((resolve, reject) => {
|
this.resolve = resolve
|
this.reject = reject
|
this.createIntervalTimer(this.duration)
|
})
|
}
|
|
private createIntervalTimer(duration: number) {
|
this.timer = setInterval(() => {
|
this.round++
|
if (this.step * this.round < duration) {
|
if (this.blockFlag) {
|
this.round--
|
clearInterval(this.timer)
|
return
|
}
|
|
try {
|
this.perStep!(this.round)
|
} catch (e) {
|
this.reject!(e)
|
}
|
} else if (this.step * this.round === duration) {
|
if (this.blockFlag) {
|
this.round--
|
clearInterval(this.timer)
|
return
|
}
|
|
try {
|
this.perStep!(this.round)
|
} catch (e) {
|
this.reject!(e)
|
}
|
clearInterval(this.timer)
|
this.resolve!('time over')
|
} else {
|
clearInterval(this.timer)
|
this.resolve!('time over')
|
}
|
}, this.step)
|
}
|
|
pause() {
|
if (!this.blockFlag) {
|
this.blockFlag = true
|
}
|
}
|
|
continue() {
|
if (this.blockFlag) {
|
this.blockFlag = false
|
clearInterval(this.timer)
|
const newDuration = this.duration - this.round * this.step
|
this.createIntervalTimer(newDuration)
|
}
|
}
|
|
/**
|
* 提前终止计时器
|
*/
|
abort() {
|
this.reject?.('abort')
|
this.reset()
|
}
|
|
/**
|
* 销毁定时器
|
*/
|
destroy() {
|
this.reset()
|
}
|
|
private reset() {
|
this.blockFlag = false
|
this.round = 0
|
clearInterval(this.timer)
|
this.timer = undefined
|
this.reject?.('restart')
|
}
|
}
|
|
export { StepTimer }
|