解决看板setInterval轮询,数据太多造成卡顿

1,571 阅读4分钟

setinterval不会清除定时器队列,每重复执行1次都会导致定时器叠加,最终卡死你的网页

setinterval和settimeout的区别

在vue.js项目中,经常需要对数据实时更新——每隔xx秒需要刷新一次接口——即需要用到定时器相关原理

我们先看一看2种常用定时器:

setInterval(function(){}, milliseconds)——会不停的调用函数

setTimeout(function(){}, milliseconds)——只执行函数一次

乍看之下,setInterval会符合我们的业务需求,然而也需要注意一些坑,单纯的使用setInterval会导致页面卡死!其原因与JS引擎线程有关,用通俗话说就是setInterval不会清除定时器队列,每重复执行1次都会导致定时器叠加,最终卡死你的网页。

但是setTimeout是自带清除定时器的,因此正确解决方法如下:

window.setInterval(() => {

setTimeout(fun, 0)

}, 30000)

注意:setInterval必须放在外层(在内层会导致页面卡顿直到崩溃),内层配合setTimeout,即可无限次调用我们的接口啦!

setInterval弊端

弊端1:setInterval会无视错误代码,即使代码报错,还是会一直执行下去。

弊端2:setInterval会无视网络延迟,很多人会有需求需要1秒更新一次数据,然后就有可能会定时发送请求给服务器请求,假如服务器请求数据发生延迟等等情况的时候,setInterval不会等到请求数据完之后才去去执行下一次请求,他会在当你发生第一次请求的时候就已经开始计时,并且无论有没有请求完成,只有时间一到下一秒的时候,就会再次发送请求。很容造成请求堵塞,或者渲染堵塞,严重的会之间卡死。

注:vue 项目的时候,如果页面使用到循环定时器,调用后台接口出现异常,在切换路由地时候并不能清除定时器。(目前我测试出来,是这样的

 beforeRouteLeave(to, from, next) {
   console.log('beforeRouteLeave')
   this.clearTimer()
   next() //一定不要忘记写
}

弊端3:很多时候越跑越快的问题。

解决方案:使用setTimeout和递归(自我调用)

例如:

使用 setInterval 方案(不推荐使用)

setInterval(function(){
   console.log('做点什么吧')
},1000)  

使用 setTimeout 方案 (推荐使用,完美代替setInterval) 注:有待验证

var demo = function(){
  console.log('做点什么吧')
  setTimeout(demo, 1000)       
}

项目是轮播一个页面多个组件的形式来展示页面中的图表,模板。一个组件模板当中有3-4个图表,定时轮播接口。但是页面经常白屏,且占用工控机的CPU资源消耗太大,固来优化性能

chrome的调试工具测试发现CPU占用特别高,了解了echarts的原理后发现是每一个图例在没有数据的时候它会创建一个定时器去渲染气泡,echarts图例是销毁了,这个echarts的实例还在内存当中,它的气泡渲染定时器也还在运行,并且定时器用的也有坑,setInterval 是一个宏任务,如果在它前面有很多任务或者某个任务等待时间较长比如网络请求报错等,那么这个定时器的执行时间和我们预定它执行的时间可能并不一致。

通过init方法创建echarts实例,如果不及时清理就会越来越多,占用大量内存,有两种方法可以避免这种情况:一种是在init之前先判断echarts实例是否存在

var chart 
if (chart == undefined) { 
chart = echarts.init(document.getElementById(dom)); 
}

另一种方法是在init之前销毁已经存在的echarts实例,可用clear()和dispose()手动清理变量,区别是clear()不会销毁实例,只是重新绘制图形,而dispose()会销毁实例,需要重新构建ECharts对象

var chart
if (chart) {
    echarts.dispose(chart)
    chart = echarts.init(document.getElementById(dom))
}

本来以为这样就ok了,过段时间切回来一看又崩溃了,诡异的是停留在当前页面查看内存一直没有涨,切换到其他网页再回来看从一个很高的内存又降到了正常值,相当于你看着它它不涨,不看它的时候一直在涨。。后来才知道chrome浏览器只有打开页面,内存才会释放,那么问题来了,如果我们想长时间查看其他页面,仍在后台运行的echarts页面很可能悄无声息地崩溃了。因此我们需要在切换到其他页面时停止自动刷新,切回来时再开启,那么有没有什么浏览器事件能在切换页面时被触发呢?答案是有的:

浏览器标签页被隐藏或显示的时候会触发visibilitychange事件 (可参考 www.jianshu.com/p/e905584f8…

我们可以监听visibilitychange事件,页面隐藏时清除定时任务,页面显示时重新开启定时任务。

document.addEventListener("visibilitychange", () => {
    if (document.hidden) {
        // 清除定时任务
    } else {
        // 开启定时任务
    }
});