
正文
定时器相关 setTimeout setInterval 函数节流
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
这个问题也是在参加百度的前端技术学院中遇到的 任务中需要用js实现动画 导师给的评价中setInterval会导致bug 当时不理解 下面把自己学习的过程分享出来
再次理解单线程
老是说js是单线程的,其实自己根本没有好好的理解好这个单线程的意思,就比如我对这个setTimeout(function(){},time)的理解就是在从现在开始到time时间点就会执行的一段代码,其实这样的理解是有问题的, 定时器只是计划代码在未来的某个时间执行,但是这个时机是无法保证的 ,这句话能更好的帮助我们理解好js的单线程( 是不是可以这样理解 就是各个代码对js单线程的争夺占用 )
其实可以把js想象是在一个时间线上运行的,我们添加的事件处理函数和ajax回调都是在这个时间线上添加一定的代码,等待执行,当前一段代码执行过后,这个时间线就处于空闲状态,就会执行后续的代码,( 在JavaScript中,没有任何代码是立刻执行的,但一旦进程空闲则尽快执行 )也就出现了这样的场景就是
var btn = document.getElementById("btn");
btn.addEventListener("click",function(){
setTimeout(function(){
console.log(1);
},1000);
/*事件处理程序的其他逻辑*/
},false);
这个例子能充分的反映出我之前的错误理解,上面的例子当响应点击事件,在1000ms后将一段代码加入了js执行的 时间线 上( 队列 ),按我之前的理解就是1000ms后console.log(1)就会执行,但是上面的例子中事件处理程序的其他逻辑的执行时间超出了1000ms,setTimeout就不是在设置完1000ms之后执行了,因为需要等待 时间线 ( 队列 )的空闲,通过上面的例子我们就很好的理解了setTimeout
2016.6.10 定时器执行的代码只有在创建它的函数执行完之后才可能被执行
setInterval
通过对setTimeout的理解,发现之前对setInterval的理解也存在问题,setInteral(function(){},time) 我的理解是间隔一定的时间段就会去执行的代码,但是这些代码之间会相互干扰吗,他们之间的切换呢 ,这些问题我都没考虑过,setInterval会导致bug我也是一头雾水
首先setInterval的功能的确是每隔一定的时间间隔在时间线(队列)中插入代码,并且js的引擎提供了这样的机制,就是当这个代码的实例已经存在这个时间线(队列)中,不会将这段代码继续的添加到时间线(队列),这样就能保证定时器的代码加入到队列中的最小时间间隔是指定的间隔,但是这种方式有一定的问题,也就是会出现bug
(1)某些间隔会被跳过
(2)多个定时器的代码执行之前的间隔可能会比预期的小
理解定时器代码实例
定时器代码的实例指的是指的是被添加到事件处理时间线(队列)中的定时器代码 于是出现了这样的场景,我们每隔200ms设置一个定时器去执行, 于是就有了下面的场景
var btn = document.getElementById("btn");
btn.addEventListener("click",function(){
setInterval(function(){
console.log(1);
},1000);
/*事件处理程序的其他逻辑*/
},false);
上面的例子中当相应btn的click事件处理程序后每隔一定的时间间隔就是在事件处理的时间线(队列)中添加一定的等待执行的代码,加入添加了第一段定时器代码后,事件处理程序仍然在运行,这个事件处理程序运行完毕后,会将js单线程的控制权交给第一段定时器代码,这个时候运行第一段定时器代码,此时由于时间线中不存在定时器代码的实例,允许添加第二段定时器代码,加入添加完第二段代码后,第一段定时器的代码仍然在运行,当需要添加第三段定时器代码的时候,由于时间线和队列中存在定时器代码的实例(也就是第二段定时器代码),这个时候就会出现这个场景第一段定时器代码执行完毕后,由于时间线中只存在第二段定时器代码,它会被立刻执行,而且上面的情况跳过了第三段定时器代码
为了避免setInterval的重复定时器的缺点,可以使用链式调用的setTimeout() 模式
setTimeout(function(){
setTimeout(arguments.callee,interval);
},interval);
优点:(1)它能保证在函数执行的过程中,在前一个定时器代码执行完毕之前,不会向时间线和队列中插入新的代码,确保不会有任何的缺失的时间间隔
(2)能保证在下一个定时器执行之前,至少等待了指定的时间间隔,避免了连续的运行
setTimeout(function(){
var div = document.getElementById("myDiv");
var left = parseInt(div.style.left) + 5;
div.style.left = left + "px";
if(left <= 200) {
setTimeout(arguments.callee,50);
}
},50);
动画的更好的实现方式 requestAnimationFrame 当我们使用setTimeout或者setInterval的时候,会有动画过度绘制,浪费 CPU 周期以及消耗额外的电能等问题,requestAnimationFrame会解决关键帧丢失的问题,并且它会把每一帧中所有的DOM操作集合起来在一次重绘中完成,并且这个重绘的时间间隔是根据浏览器的刷新频率的并且它不会对隐藏或者不可见的频率进行重绘,也就减少了CPU和内存的使用
var demo = document.getElementById("myDiv");
function render() {
demo.style.left = parseInt(demo.style.left) + 5 + "px";
}
requestAnimationFrame(function(){
render();
if(parseInt(demo.style.left) <= 300) {
requestAnimationFrame(arguments.callee);
}
});
我对比了setTimeout和requestAnimationFrame 这两种模式 发现 requestAnimationFrame这种的确更流畅
参考 JavaScript高级程序设计
性能更好的js动画实现方法--requestAnimationFrame
2016 7 24 Raf 相关学习
javascript高性能动画与页面渲染
2016 9 28 函数节流 之前讨论的问题的每个一定的时间间隔去执行一定的代码 函数节流是某段代码不可以在没有间断的情况下连续的执行 这个方式可以用在一些不能频繁操作的地方 例如会造成大量DOM操作的事件上 (输入联想等 )
思路 第一次调用函数的时候,创建一个定时器 在指定的时间间隔之后运行代码 当第二次调用该函数的时候 它会清除之前的定时器 如果之前的定时器已经执行过了,就相当于新加入一个定时器 否则就是取消了之前的操作 新加入定时器 就能避免短时间的频繁的操作
模式
function throttle(method,context) {
clearTimeout(emthod.tId);
method.tId = setTimeout(function(){
method.call(context)
},1000);
}






