/**
 * 使用 setTimeout() 代替setInterval()，支持同步/异步方法，会等待异步执行完毕之后在发起请求
 * @example
 * ```tsx
 * const count = ref(5);
 * const loop = new AsyncLoopExec({
 *   run() {
 *     if (count.value <= 0) {
 *       loop.stop();
 *       return;
 *     }
 *     count.value--;
 *   },
 *   delay: 1000,
 *   immediate: false
 * });
 * loop.start();
 * ```
 */
export default class AsyncLoopExec {
  private isStop = true;
  private run: () => void | Promise<any>;
  private delay: number;
  private immediate: boolean;
  private _immediate: boolean;
  private timer: any;
  /**
   * immediate 表示是否初始化就执行，默认 true
   */
  constructor({
    run,
    delay,
    immediate = true
  }: {
    run: () => void | Promise<any>;
    delay: number;
    immediate?: boolean;
  }) {
    this.delay = delay;
    this.run = run;
    this.immediate = immediate;
    this._immediate = immediate;
  }
  private async _run(...args: any[]) {
    // 防止方法为异步，导致 stop 方法的 clearTimeout 无效，需要再加上一个判断
    if (this.isStop) {
      clearTimeout(this.timer);
      return;
    }
    this.immediate && (await this.run());
    this.timer = setTimeout(() => {
      this.immediate = true;
      this._run(...args);
    }, this.delay);
  }
  start(...args: any[]) {
    if (!this.isStop) return;
    this.immediate = this._immediate;
    this.isStop = false;
    clearTimeout(this.timer);
    this._run(...args);
  }
  stop() {
    this.isStop = true;
    clearTimeout(this.timer);
  }
}
