记录一下轮询业务

226 阅读2分钟

业务需要在app上添加一个报警功能,后台管理系统这边决定要进行一个轮询时间是30S.后台管理系统我使用的是vue-element-admin 思路是登录进入el-mian页面就开始执行这个延时器并且开始轮询.为了防止多个请求并发的情况没用用定时器而是使用延时器并递归.

image.png

首先我们需要一个全局定时器来在该组件销毁的时候将这个定时器也清空

data() {
    return {
      // 定时器
      timer: '',
    }
  },

然后是逻辑部分

   // 轮询方法
    lunxun() {
      this.timer = setTimeout(async() => {   //绑定延时器
        const { data } = await getMyWarnings()
        if (data.status) {
          this.serviceList = data.serviceList
          this.serviceList.forEach(item => {
            this.notifyPromise = this.notifyPromise.then(() => {
              this.$notify({
                title: '警告',
                dangerouslyUseHTMLString: true,
                message: `服务人员${item.name}报警请尽快点击处理!!!`,
                type: 'warning',
                // 点击事件
                onClick: () => {
                  this.personalId = item.personalId
                  this.personalName = item.name
                  this.personalPhone = item.phone
                  this.dialogWarning = true
                }
              })
            })
          })
        }
        clearTimeout(this.timer)  //开始递归前清空延时器,也可以放在该函数开始前看自己
        this.lunxun()  //递归
      }, 30000)
    },
    //分别在dom加载完成和组件销毁的时候创立和销毁
      mounted() {
    // 开始轮询
    this.lunxun()
  },
      beforeDestroy() {
    // 清除定时器
    clearTimeout(this.timer)
  },

除了这个需求呢还有一个联系商家的请求,业务是拨打电话(当前业务并不是直接拨打而是后台给该服务人员电话号码,这里的服务电话号码是虚拟号,绑定时间大概是一分钟)点击联系商家的按钮会出现一个对话框,将电话号码放入即可. 拨打电话的代码

image.png

    async call() {
      const { data } = await callCustomerBySys({ 'orderId': this.orderId })
      if (data) {
        this.$alert('请拨打此电话联系客户: ' + data.phone, '客户电话', {
          confirmButtonText: '确定',
          callback: action => {
            this.$message({
              type: 'info',
              message: `action: ${action}`
            })
          }
        })
      }
    },

后续需求是要求展示对话状态,因为是虚拟号所以有四个状态(未绑定,已绑定,拨打中,拨打完成)这里我们需要进入这个订单详情组件的时候就开始第一次的轮询,因为有可能客服人员点击了拨打电话但是又退出去了.第一次的请求我直接放在了获取详情的函数中.这里会判断是否是已绑定或者拨打中状态,如果是这两种状态就会进行轮询,也就是递归循环.

这里是判断的代码

    // 查询通话状态value数据,string为判断是否为套餐的参数
    async QueryingUserStatus(value, string) {
      if (string === 'service') {
        // 单个服务
        const { data } = await callPersonalBySysToStatus({ orderId: value.id })
        this.Callstatu = data
        if (data === '已绑定' || data === '拨打中') {
          // 单个轮询
          this.lunxun(value.id)
        }
      } else {
        // 套餐需要循环
        value.forEach(async(item) => {
          const { data } = await callPersonalBySysToStatus({ orderId: item.id })
          // 将通话状态放入对象
          this.$set(this.Callstatuarrly, item.id, data)
          this.$set(this.timerarrly, item.id, null)
          if (data === '已绑定' || data === '拨打中') {
            // 多个轮询
            this.packagelunxun(item.id)
          }
        })
        console.log(this.Callstatuarrly)
        console.log(this.timerarrly)
      }
    },

因为业务中有单个订单和套餐的情况,套餐有可能是两个服务.两个服务有可能是两个服务人员.经过与后端的沟通决定由服务订单id来获取该用户的电话号码,也就是说这边需要我传服务id过去.我就单个多个服务进行了区分走不同的请求.这个功能是后续加的,就是说这个字段不在订单详情返回的数据里.而且我也不知道套餐里面有几个服务,这就需要在第一次循环套餐的时候建立一个对象把服务id当key请求回来电话数据作为值展示在页面上,这样通过(obj[id])就可以展示当前的电话拨打状态,这里注意不要用obj.id 这里id是动态的!!!就算是对象也可以用[]来取值

image.png

定时器也是一样,单个订单很容易进行轮询并在组件销毁的时候销毁这个延时器函数.但是套餐的情况下就要复杂一点,不知道有几个子订单的情况下要怎么设置足够的定时器呢?在进入组件获取订单详情的时候我们会获取到该订单是套餐还是单个订单,单个订单我们会直接处理套餐的话会进行循环,就在循环的时候动手!

      // 单个服务的通话状态
      Callstatu: '',
      // 多个服务的通话状态
      Callstatuarrly: {

      },
      // 轮询查询通话状态单个服务定时器
      timer: [],
      // 轮询查询通话状态套餐多个定时器
      timerarrly: {

      }
      //!!!!重点
       async QueryingUserStatus(value, string) {
      if (string === 'service') {
        // 单个服务
        const { data } = await callPersonalBySysToStatus({ orderId: value.id })
        this.Callstatu = data
        if (data === '已绑定' || data === '拨打中') {
          // 单个轮询
          this.lunxun(value.id)
        }
      } else {
        // 套餐需要循环
        value.forEach(async(item) => {
          const { data } = await callPersonalBySysToStatus({ orderId: item.id })
          // 将通话状态放入对象
          this.$set(this.Callstatuarrly, item.id, data)
          this.$set(this.timerarrly, item.id, null)
          if (data === '已绑定' || data === '拨打中') {
            // 多个轮询
            this.packagelunxun(item.id)
          }
        })
        console.log(this.Callstatuarrly)
        console.log(this.timerarrly)
      }
    },
    // 单个轮询
    lunxun(id) {
      clearTimeout(this.timer)
      this.timer = null
      this.timer = setTimeout(async() => {
        console.log('单品')
        // 单个
        const { data } = await callPersonalBySysToStatus({ orderId: id })
        this.Callstatu = data
        this.$forceUpdate()
        if (data === '已绑定' || data === '拨打中') {
          this.lunxun(id)
        }
      }, 5000)
      this.$once('hook:beforeDestroy', () => {
        clearTimeout(this.timer)
        this.timer = null
        console.log('单个定时器清空')
      })
    },
    // 套餐轮询
    packagelunxun(id) {
      clearTimeout(this.timerarrly[id])
      this.timerarrly[id] = null
      this.timerarrly[id] = setTimeout(async() => {
        console.log('套餐')
        // 套餐
        const { data } = await callPersonalBySysToStatus({ orderId: id })
        this.Callstatuarrly[id] = data
        this.$forceUpdate()
        if (data === '已绑定' || data === '拨打中') {
          this.packagelunxun(id)
        }
      }, 5000)
      this.$once('hook:beforeDestroy', () => {
        clearTimeout(this.timerarrly[id])
        this.timerarrly[id] = null
        console.log('套餐定时器清空')
      })
    },

这里我还百度到了清空延时器的新方法就是监听组件销毁事件的时候处理,但是可能有点问题.这里我是在第一次进入订单详情的时候就确定了定时器的数量并定义到data的对象内.然后根据订单的id来当key .用传入的id来对应延时器函数,每次进去的时候我都会清空上一个延时器.业务是点击联系商家的按钮就会传入该id来调用这个轮询方法,当然是单个走单个轮询,套餐走套餐轮询.不过清空的时候发现了一个问题就是有几个定时器(每次循环销毁的)就会打印几次"定时器清空"不知道是不是这个监听函数的问题.如果想避免的话就可以直接循环定时器对象来清空.