今天搞个东西,遇到点关于 `setInterval` 的小麻烦,记录一下省得以后再踩坑。
是这么回事,我当时在做一个页面,需要同时更新好几个地方的数据。比如说,一个地方要显示个实时时间,另一个地方,隔几秒钟就要去后台看看有没有新消息。我一开始图省事,也没多想,心说这不就俩定时器嘛简单。
直接上手
我就直接在代码里写两个 `setInterval`。一个设1秒,更新时间;另一个设比如5秒,去请求数据。
刚开始跑起来,看着还行,时间在一秒一秒地跳,数据看着也在更新。心里还挺美,觉得挺顺利。
发现问题
但是跑一段时间,尤其是我切到别的页面再切回来的时候,怪事就发生。那个时间跳得好像越来越快?有时候甚至感觉一秒跳好几下。然后那个请求数据的,我一看网络请求记录,嚯,好家伙,密密麻麻的,请求频率明显比我设定的5秒要快得多,有时候甚至一秒发好几次!
这时候我就感觉不对劲,页面也变得有点卡顿。我就琢磨,这咋回事?明明设定好的时间间隔,怎么就乱套。
排查过程
我就开始检查代码。一开始怀疑是不是逻辑写错,导致函数执行好几次。看半天,逻辑 вроде бы ( вроде бы means "seemingly" or "apparently" in Russian, used here colloquially like "kinda") 没啥大问题。
后来我尝试着在定时器函数里面加点打印信息,每次执行都输出点东西。结果不看不知道,一看吓一跳。每次我离开页面再回来(我用的是那种单页应用,页面切换是组件的卸载和重新加载),控制台里打印的信息就多一份!这说明说明之前的 `setInterval` 没停掉,新的又启动!
好家伙,等于说我每次回来,都新加一个定时器在跑,原来的还在那儿傻跑。这不就叠加嘛怪不得越来越快,请求越来越多,页面越来越卡。
- 第一次进来:1个时间定时器,1个数据定时器。
- 切走再回来:变成2个时间定时器,2个数据定时器。
- 再切走再回来:变成3个时间定时器,3个数据定时器...
这谁受得。
解决办法
找到问题就好办。我想起来 `setInterval` 这东西是会返回一个ID的,可以用 `clearInterval` 来清除它。
关键点来:
必须在组件卸载或者下次准备启动新的定时器之前,把上一个定时器给清除掉。
具体做法就是:
- 用一个变量(比如挂在组件实例上,或者用个全局点的变量,看情况)来保存 `setInterval` 返回的ID。
- 在组件卸载的生命周期钩子(比如 React 的 `useEffect` 的清理函数,或者 Vue 的 `beforeDestroy`/`unmounted`)里面,调用 `clearInterval(保存的ID)`。
- 或者,如果是在同一个组件内重复设置定时器,那就在每次设置新的 `setInterval` 之前,先判断一下那个保存ID的变量是不是有值,有的话就先 `clearInterval` 清掉旧的,再设置新的。
比如像这样(伪代码示意一下):
let timerId = null;
function startMyTimer() {
// 先清旧的
if (timerId) {
clearInterval(timerId);
}
// 再设新的
timerId = setInterval(() => {
// ...干活...
}, 1000);
// 在不需要的时候,比如组件销毁时
function cleanup() {
if (timerId) {
clearInterval(timerId);
}
效果
把这些都改好之后,再测试。果然利索!不管我怎么切换页面,回来之后定时器都按预期的频率在跑,不多不少,页面也流畅。
总结一下就是: 用 `setInterval` 千万要记得擦屁股,不用的时候或者要重新设置的时候,一定得用 `clearInterval` 把之前的给清掉,不然它就一直在后台跑,越积越多,迟早出问题。算是给自己提个醒。