前端vue、react 实现接口轮训

前端实现接口轮类

监听路由变化
const { history } = window

// 响应pushState和replaceState的监听事件名称
export const STATE_CHANGE_NAME = 'stateChange'

/**
 * 重写history的pushState和replaceState
 * @param action {String} pushState|replaceState
 * @return {Function}
 */
function wrapState(action) {
  // 获取原始定义
  const raw = history[action]

  return function () {
    // 经过包装的pushState或replaceState
    const wrapper = raw.apply(this, arguments)
    // 定义名为action的事件
    const e = new Event(STATE_CHANGE_NAME)
    // 将调用pushState或replaceState时的参数作为stateInfo属性放到事件参数event上
    e.stateInfo = {
      action,
      path: arguments[2],
    }
    // 调用pushState或replaceState时触发该事件
    window.dispatchEvent(e)
    return wrapper
  }
}

// 修改原始定义
history.pushState = wrapState('pushState')
history.replaceState = wrapState('replaceState')

/**
 * 监听并响应路由变更事件
 * @param {*} fc 响应事件
 * @param {*} options
 * @returns {Function} 移除事件函数
 */
export default function useRouteChange(fc, options = {}) {
  const { hashChange = true } = options
  const routeChange = (event) => {
    const { singleSpaTrigger } = event
    // NOTE:pushState & replaceState 单独交给 STATE_CHANGE_NAME 事件去监听处理
    if (['pushState', 'replaceState'].includes(singleSpaTrigger)) return

    typeof fc === 'function' && fc(event)
  }

  window.addEventListener('popstate', routeChange, false)
  window.addEventListener(STATE_CHANGE_NAME, routeChange, false)
  hashChange && window.addEventListener('hashchange', routeChange, false)

  const remove = () => {
    window.removeEventListener('popstate', routeChange)
    window.removeEventListener(STATE_CHANGE_NAME, routeChange)
    window.removeEventListener('hashchange', routeChange)
  }

  return remove
}

Polling类(接口轮训)
import useRouteChange from './useRouteChange'

const BASE_OPTIONS = {
  delay: 5000, // 默认5s一轮询
  stopWhenRouteChange: true,
}

function nulloop() {}

/**
 * 接口轮询类
 * - 启动轮询
 * - 关闭轮询
 * - 页面visible切换时 自动停止轮询
 */
export default class Polling {
  /**
   * @param {Function} fc Promise化的待轮询方法,通常为一个http请求方法
   * @param {Object} options 配置项
   */
  constructor(fc, options) {
    this.fc = typeof fc === 'function' ? fc : nulloop
    this.timer = null // 轮询定时器
    this.options = {
      ...BASE_OPTIONS,
      ...(options || {}),
    }
    this.isPolling = false // 是否在轮询中
    this.removeRouteChange = null

    window.addEventListener('visibilitychange', this._onVisibilityChange, false)
    if (this.options.stopWhenRouteChange) {
      this.removeRouteChange = useRouteChange(this._onRouteChange)
    }
  }

  // 添加 routerchange 时停止轮询的逻辑
  _onRouteChange = (event) => {
    console.log('[polling]_onRouteChange:', event)
    this.stop()
  }

  _onVisibilityChange = () => {
    if (document.hidden) {
      console.log('[polling]页面被隐藏了')
      this._innerStop()
    } else {
      console.log('[polling]页面重新激活了')
      // 手动中断了 则不再继续轮询
      if (this.isPolling) {
        this.start(true)
      }
    }
  }

  async _exec() {
    try {
      await this.fc()
    } catch (e) {
      console.error('[polling] error: ', e)
    }
  }

  _innerStop() {
    this.timer && clearTimeout(this.timer)
    this.timer = null
  }

  /**
   * 开始启动轮询(对外暴露API)
   * @param {Boolean} immediate 是否立即执行方法(通常无需传参)
   * @returns
   */
  async start(immediate = false) {
    // 正在轮询中则无需重复调用
    if (this.isPolling && !immediate) return

    console.log('[polling]...start...')
    this.isPolling = true
    this._innerStop()
    await this._exec()

    const loop = () => {
      this.timer = setTimeout(async () => {
        // 还在轮询中才继续执行
        if (this.isPolling) {
          await this._exec()
          loop()
        }
      }, this.options.delay)
    }

    loop()
  }

  /**
   * 中断/暂停轮询(对外暴露API)
   */
  stop() {
    console.log('[polling]...stop...')
    this._innerStop()
    this.isPolling = false
  }

  /* 销毁轮询实例 */
  destory() {
    window.removeEventListener('visibilitychange', this._onVisibilityChange)
    this.removeRouteChange && this.removeRouteChange()
    this.stop()
    this.fc = nulloop
  }
}
使用教程
// 引入Polling类
import Polling from '@/utils/polling'

/**
  * 第一个参数传入一个函数,该函数为将要轮训接口的方法
  * 第二个参数传入配置项入轮训时间等
  * 该类对外暴露pollingInst.start(), pollingInst.stop(), pollingInst.destory() 三个方法
 */
const pollingInst = new Polling(pollingResult, {}) 


// 样例
const queryDataExtractionParams = async () => {
  const [err, res] = await to(queryDataExtractionParamsApi({ recordId }))
  if (err) return
  Object.assign(pageParams, res.data)
  sqlEditor.value.setValue(res.data.customerSql)
  if (res.data) pollingInst.start() // 开始轮训
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值