定时器使用不当造成的问题

情景描述

  • 需要在某个操作执行成功之后
  • 开始倒计时五秒弹回首页
  • 因此设置了一个每隔1s执行一次setState将显示在屏幕上数字减一
componentWillReceiveProps(nextProps) {
    if (nextProps.isChangePasswordSucceed) {
      this.timerID = setInterval(::this.tick, 1000)
    }
  }
  
tick() {
    if (this.state.timer > 0) {
      this.setState({
        timer: this.state.timer - 1
      })
    } else {
      clearInterval(this.timerID)
      this.props.actions.jumpHome()
    }
  }
  • 但却发现显示在屏幕上的数字直接从5到了0,然后发现tick的else仍然一直在执行

知识补充

  • Q1:setTimeout和setInterval的返回值是什么?有什么作用
  • A1:他们的返回值都是当前定时器的ID,在用一个window或者frame中每当创建一个新的定时器(不论是setInterval还是setTimeout)都会在内存中创建一个定时器,他们的ID都是按照创建顺序依次加一的,并且setTimeout和setInterval的定时器ID都是在同一个ID池中,因此如果先创建了一个setTimeout的ID是1,那么下一个创建的setInterval的ID是2
  • Q2:那么这些定时器的ID有什么作用呢??
  • A2:作用大了!!!
    • 1.作为clearInterval或者clearTimeout的参数将定时器在内存中完全清除
    • 2.当定时器出现执行间隔变短,或者其他异常情况可以通过打印定时器ID的方式检测是否创建了多个定时器

问题分析

  • Q1:很显然,定时器的执行间隔远远小于1s,这是为什么呢?

  • A1:那么这里剖析一下为什么setInterval的执行

    • 首先创建一个定时器(每隔1s执行一次)
    • 过了1s之后,会将操作放入异步执行队列
    • 如果此时主线程没有工作
    • 那么会立刻执行这个操作(以上是正常的执行情况)

    • 定时器做得事情是:
      • 每隔1s查看异步执行队列中有没有同一个定时器的function
        • 没有:将fucntion写入异步执行队列
        • 有:就不将function写入(简而言之:这一次本应该执行的fucntion被直接跳过了),直接忽略
    • 主线程做得事情是:
      • 手头工作一旦完成立刻查看异步执行队列
      • 按照一致队列的顺序依次执行队列中的任务
    • 现在来分析一下为什么定时器会加速
      • s1:当主线程一直有东西在执行,
        • 导致第一次定时器的执行就晚了一些
        • 然后定时器的functin执行时间又小于1s
        • 并且在此期间已经在异步队列中插入了下一次的fucntion
        • 会导致第一次的执行和第二次的执行间隔小于1s
      • s2:创建了多个定时器
        • 导致相似的时刻有多个同样但是来自不同定时器的function被加入异步队列
        • 恰巧每个function的执行时间很短,会导致1s内执行了多个function
    • 因此总结来说,发现定时器加速执行,可以先判断第二种情况是否发生了
      • 判断方式:通过打印定时器的ID看看当前tab中出现的到底是一个ID还是多个ID
  • Q2:为什么tick的else会执行说明一定执行clearInterval,那么为什么定时器还在呢?

  • A2:这就更加说明了你创建了多个定时器,创建定时器的时候通常会将定时器的ID赋给一个值(比如本文中的this.timerID)然后使用它作为clearInterval的参数。然而

    • 当你创建了多个定时器
    • 这时候this.timerID一定指向的是最后一个定时器
    • 就说明之前创建的定时器就在内存在但是已经没有引用了
    • 所以之前创建的那些定时器,除非你刷新页面更新window对象,否则永远没有办法把他停止了
  • 所以!!注意检查定时器的ID很重要

  • Q3:那么是什么造成创建了那么多次的定时器呢?

  • A3:这就是使用componentWillReceiveProps中必须要注意的地方:

    • componentWillReceiveProps函数只要props数据变化就会被调用
    • 因此我们通常使用if包住想在其中执行的代码
    • 但是如果if每次都被判为true就会导致代码块被反复执行
    • 所以在componentWillReceiveProps中执行函数,一定要有万全的判断并且考虑如果这么判断代码块会被执行几次
    • 总结:想在componentWillReceiveProps中创建定时器或者弹出模态框等操作一定要考虑如何进行if判断能保证这个操作只会被执行一次

      • 必须要让if的判断更加全面
  • 当前解决,就是当this.timerID不再是undefined那么就不能再进入if true了

你可能感兴趣的:(定时器使用不当造成的问题)