Source: polling.js

/**
 * 本模块提供轮询功能。
 * @module polling
 */

import { theGlobal } from './internal/core';
import { extend } from './lang';

/**
 * 轮询类。
 * @memberof module:polling
 * @class
 * @name Polling
 * @constructor
 * @param {Function} executor 执行函数,返回值为 Promise(带有 then 方法)时会进行异步处理。
 * @param {Object} [options] 其他选项。
 *   @param {number} [options.interval=1000] 轮询间隔(毫秒)。
 *   @param {boolean} [options.breakOnError=false] 执行函数有异常(包括 Promise 的拒绝状态)时是否中断轮询。
 * @example
 * const polling = new Polling(() => {
 *   return new Promise((resolve) => {
 *     setTimeout(() => {
 *       console.log('executed');
 *       resolve();
 *     }, 1000);
 *   });
 * }, {
 *   interval: 2000
 * });
 * polling.start();
 */
export default class Polling {
  constructor(executor, options) {
    // 执行函数
    this._executor = executor;
    // 其他选项
    this._options = extend({
      interval: 1000,
      breakOnError: false
    }, options);
    // 轮询计时器 id
    this._timerId = null;
    // 轮询是否已开始
    this._started = false;
    // 是否正在运行执行函数(针对异步情况)
    this._isExecuting = false;
    // 是否要在当前轮询之后马上运行执行函数
    this._shouldImmediate = false;
  }

  // 执行轮询函数
  _exec() {
    let result;
    try {
      result = this._executor.call(theGlobal);
    } catch (e) {
      if (this._options.breakOnError) {
        this.stop();
      }
    }

    if (result && typeof result.then === 'function') {
      // 异步情况,在 Promise 回调中继续下一次轮询
      this._isExecuting = true;
      result.then(() => {
        this._isExecuting = false;
        this._next();
      }, () => {
        this._isExecuting = false;
        if (this._options.breakOnError) {
          this.stop();
        } else {
          this._next();
        }
      });

    } else {
      // 同步情况,直接执行下一次轮询
      this._next();
    }
  }

  // 下一次轮询
  _next() {
    if (this._shouldImmediate) {
      // 外部调用了 execImmediately,马上执行
      this._exec();

    } else if (this._started) {
      // 进入下一次轮询
      this._timerId = setTimeout(() => {
        this._exec();
      }, this._options.interval);
    }
  }

  /**
   * 在当前轮询结束后马上执行一次执行函数。
   * @method
   * @memberof module:polling.Polling.prototype
   */
  execImmediately() {
    // 阻止下次轮询
    this._clearTimeout();

    if (this._isExecuting) {
      // 如果当前轮询还在运行中,先记录下来,待结束后再执行
      this._shouldImmediate = true;
    } else {
      // 下一次轮询还没开始,直接运行执行函数
      this._exec();
    }
  }

  /**
   * 启动轮询。
   * @method
   * @memberof module:polling.Polling.prototype
   */
  start() {
    this._started = true;
    this._exec();
  }

  /**
   * 停止轮询。
   * @method
   * @memberof module:polling.Polling.prototype
   */
  stop() {
    this._clearTimeout();
    this._started = false;
  }

  // 清理计时器
  _clearTimeout() {
    if (this._timerId) {
      clearTimeout(this._timerId);
      this._timerId = null;
    }
  }
}