情景描述
- 需要在某个操作执行成功之后
- 开始倒计时五秒弹回首页
- 因此设置了一个每隔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被直接跳过了),直接忽略
- 每隔1s查看异步执行队列中有没有同一个定时器的function
- 主线程做得事情是:
- 手头工作一旦完成立刻查看异步执行队列
- 按照一致队列的顺序依次执行队列中的任务
- 现在来分析一下为什么定时器会加速
- s1:当主线程一直有东西在执行,
- 导致第一次定时器的执行就晚了一些
- 然后定时器的functin执行时间又小于1s
- 并且在此期间已经在异步队列中插入了下一次的fucntion
- 会导致第一次的执行和第二次的执行间隔小于1s
- s2:创建了多个定时器
- 导致相似的时刻有多个同样但是来自不同定时器的function被加入异步队列
- 恰巧每个function的执行时间很短,会导致1s内执行了多个function
- s1:当主线程一直有东西在执行,
- 因此总结来说,发现定时器加速执行,可以先判断第二种情况是否发生了
- 判断方式:通过打印定时器的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了
网友评论