不知道你是否在工作中遇到过,当你离开浏览器 tab
的时候,可能你去干其他事情去了,有可能你打开网页,下班的时候忘记关了,电脑也忘记关了,但很不幸的事,当你再次打开 tab
的时候发现页面已经崩溃了,或者页面变得比之前更卡了。
当我遇到这个问题的时候,我第一时间想到的是,内存泄露;于是我打开了内存分析工具,我足足分析了一个小时也没有发现内存存在泄露,每一次内存释放都能回到之前的内存量,于是我开始困惑了,于是我便开始分析代码,看了好一会儿也没发现有啥端倪。这个时候我便放下之前的方式,想想之前线上会不会存在这样的问题,于是我打开线上环境,进行了测试,但是还是一无所获。于是我告诉了测试,让测试测试之前的版本。哪天下班了,测试像往常一样操作,当再次上班的时候发现问题不存在。然后我便开始分析新增的代码,不知道找了多久,把所有的怀疑都验证了,都不是问题的关键。一次偶然的机会让我察觉到了问题的存在。
因为我是一直开着这个性能检测器的。我苦思冥想也想不出,我便一边百度一边思考;这次很意外,我思考的比较久,当我再次回到这个界面的时候,发现上面的 JS 推大小
居然变大了很多,但是不一会儿内存又回到了正常水平,于是我就开始怀疑是因为离开了这个页面导致的;接下来我便开始刻意离开页面,然后观察内存使用情况,发现果然如此。我们的代码逻辑是一直接收 base64
的照片,然后渲染出来,看起来就像一个视频,我们的 base64
的照片是设备上报来的,要想设备上报这些数据,需要使用串口跟设备建立连接。但是当我离开界面经过一段时间再次回来的时候,即便我断开与设备的连接,但是图像仍然一直在动,说明此时还有很多逻辑没有处理,一直被堆积,所以即便切断了来源,水还是一直在流,也就是离开页面以后水是流在蓄水池里面,并没有直接消化掉。基于这个结论我再次分析了代码,最后把代码锁定在下面这几行:
JavaScript
// 模拟源源不断的水
setInterval(() => {
console.log(i ++)
requestIdleCallback(() => {
console.log("这里处理图片的相关操作")
})
}, 2000)
其中对图片的逻辑操作由于存在一定的逻辑,担心慢,也是因为出现过慢的情况(其实后面排查发现是设备导致的慢,跟我这边无关,只不过代码已经改了),于是我就想不要采用同步的方式执行,而是改成异步,但是我又不想使用 setTimeout
,于是想到使用这个 requestIdleCallback
来执行,但是我对这个 api
的理解仅仅停留在能调用的水平,不知道的是,这个 api
当 tab
标签处于非激活状态下时,回调不会被执行。那在非激活状态下这些回调怎么处理呢,很简单就是将所有的回调存起来,当 tab
再次激活的时候执行。
其中 127
这个就是当从未激活到激活时迅速消化掉的日志。如果你的回调里面处理的逻辑不多,有时候你是察觉不到的,因为你切回来的瞬间几乎就能消化掉之前的。只有长时间处于非激活状态,你才能看出内存的变化。如果非要使用这个 api
,一定要配合这个 api
的第二个参数,也就是 option
。把代码改成下面这样就不会存在问题了,当然对于这种代码最好还是不要采用这样方式来解决。
JavaScript
// 模拟源源不断的水
setInterval(() => {
console.log(i ++)
requestIdleCallback(() => {
console.log("这里处理图片的相关操作")
// 这里新增一个超时的参数
}, {timeout: 100})
}, 2000)
如果这样,即便我们的 tab
处于非激活状态也会被执行。,当然这并非保险的做法,对于我的需求来说,最好不要使用这种办法,如果真的很费时,那么采用 setTimeout
就很不错。
由于我只是在 Chrome
中进行测试和操作,所以以上都是 Chrome
浏览器的,其他的浏览器需要当事人进行充分测试才能保证其正确性。