14-面试讲解
# 计时器节流问题
# 面试讲解
# 知识点图谱
- 浏览器计时节流
- WebWorker
- window.URL.createObjectURL
- Websocket
# 难点描述
**模拟问题:**我看你的简历里面写了有一个亮点是对浏览器计时器节流的处理,并做了封装,这个你能详细说一下吗?
问题分析:
首先应该告诉面试官,是什么样的需求,问题的思考过程,以及解决办法,最后的结果
参考答案:
**(需求描述)**在我们的项目中,有这样子的需求,如果长时间不对页面进行编辑,就自动帮你退出编辑状态。
这个需求刚刚开始做的时候,其实重心是放在和后端沟通,因为需求为了保证当前时间只有唯一用户进行编辑,对当前编辑用户还采用了心跳检测。怎么样发送心跳检测防止多个用户产生同时编辑的情况,当然确定的方案是setInterval轮询的方式(埋钩子:可能会对面试官产生兴趣,既然心跳检测了为什么不直接使用Websocket)
(解决问题)刚开始就打算使用
setInterval进行计时,功能这些都没什么问题,但是测试的时候偶尔发现时间不是太准确,虽然我知道使用setInterval做动画的时候计时肯定是不准确的,造成这种情况的问题很多,比较的复杂(埋钩子:引导面试问出你想回答的问题),但是这种长时间不操作的检查,我们并没有要求太严格的时间要求,但是测试发现这个不准确的时候非常的夸张。应该不仅仅是动画计时不准确的原因最后发现,问题发生在当前页面失活的情况下。查询了相关资料,现代浏览器都是多标签页的情况,为了大量多标签出现,严重影响耗电量和电池续航时间,以及内存的占用。专门处理了相关代码,减少后台工作,而后台运行的计时器就是这种优化的一个手段
简单来说,对于非活动选项卡,它们会自动限制计时器每 1 秒运行一次,而不管代码中指定的原始延迟是多少。例如,如果代码最初使用setInterval()每 50 毫秒运行一次某些代码,一旦应用程序移至后台选项卡,间隔就会自动变为 1000 毫秒(1 秒),随着时间的推移,时间差距也会慢慢拉大。然而,一旦重新激活选项卡,原始间隔或延迟就会返回到原始值。
所以,为了解决这个问题,我使用了
WebWorker的API,把计时器直接放在另外的线程中,就不会有这样的问题了,通过主线程与WebWorker线程之间进行通信,发送消息来维持WebWorker线程中计时器的开关。**(深度思考)**但是这样处理,对于开发其实是有心智负担的,我们的目的其实也就仅仅是通过计时器做简单计时。但是为了解决失活标签页计时器节流的问题,不得不把计时器放入到
WebWorker线程,而计时器的开关,我还得通过发送开关消息到WebWorker线程进行处理。因此我把
WebWorker线程做了封装,通过动态生成WebWorker的脚本(埋钩子,引导面试官提问),在主线程劫持模拟setTimeout与setInterval,在WebWorker线程中监听主线程发送的信息,并执行对应的计时器,然后在主线程监听消息,并做出对应处理即可。这样封装之后,直接在我们的代码中使用计时器即可,没有任何的感觉,不必担心再出现浏览器计时器节流的问题,极大的减少了我们开发的心智负担。
(整个过程就是这样,看面试官您还有什么要问题,我再补充)
# 知识点叙述
1、动态生成WebWorker的脚本
**模拟问题:**你刚刚提到了,动态生成WebWorker的脚本,你是怎么做的?
问题分析:
这个基本上是我们埋下的钩子,引导面试官来问这个问题,也是具体操作的延续
参考答案:
其实主要就是利用了window.URL.createObjectURL(blob) (opens new window)这个API,这个API我们在做上传下载预览的时候经常用到,当然现在的Blob (opens new window)对象需要我们自己创建。然后在主线程劫持模拟
setTimeout与setInterval,其实就是覆盖原有的相关方法,内部改成worker.postMessage给WebWorker线程发送数据,并且通过对象缓存记录计时器的回调函数和timer参数,所以发送给WebWorker线程的数据其实就是一个唯一id在
WebWorker线程中监听主线程发送的信息,并执行对应的计时器,同时发送回消息给主线程主线程接收到消息,通过
WebWorker线程计时器不断发送过来的信息,找到缓存对象中的回调函数,进行执行即可
2、Page Visibility API
**模拟问题:**如果当前页签失活了,如何判断当前页签又激活了呢?
问题分析:
一个基础性的问题
参考答案:
直接通过
Page Visibility API就行了,在document上添加事件监听addEventListener('visibilitychange', function() {......}),可以通过属性document.visibilityState获取当前的状态是hidden,还是visible当然,在具体的业务中,页签的反复切换并不能直接确认当前页面是否在编辑状态,需要激活具体的事件才会又重新计时,因此需要对相应的事件进行监听,事件激活之后又重新开始计时。
3、setInterval/setTimeout计时不准确
**模拟问题:**你刚刚说到setInterval/setTimeout计时不准确是什么意思?
问题分析:
这个问题,是故意引导给面试官的一个引导性问题,可以介绍计时器一起卡顿的具体效果原因。
但是,主要目的就是引导面试官去问你想回答的问题,比如:浏览器渲染策略,事件循环等等,
参考答案:
setTimeout、setInterval 属于定时触发器线程属于macrotask,它的回调会受到浏览器渲染、事件循环、http请求等等的影响。
浏览器的画面,是一帧一帧的渲染出来的,每隔一小段时间,会在页面上画一下,只不过画的太快,我们很难感觉出来而已。理想情况下,一秒钟绘画60次,也就是60帧,也就是16.6毫秒画一帧(1000/60)。
setInterval的回调函数并不是到了时间立即执行,而是等系统计算资源空闲下来后才会执行。 下一次触发时间是在setInterval回调函数执行完毕后才开始的
由于计时器的计时并不精确,就可能造成空帧,或者一帧中多次计时到达。所以如果我们用计时器做动画的时候,就有卡顿的感觉。
(当然,具体为什么会计时不准确,那我们需要仔细的去梳理浏览器渲染、事件循环相关的知识点了,面试官你看这个需要详细说一下吗?)