javascript定时器的那些事儿

时间:2021-04-19 00:11:08

一直认为学会的东西后来才发现了解的仅是一点点~还有很多需要我去了解、探索,从书籍、博客、团队的伙伴、视频等等一系列都可以学到很多。只要你想就会变得更加强大,不断积累自己吧~~~

今天就了解一下定时器,先看看下面代码

document.onclick = function(){
    setTimeout(function() { console.log("111"); }, 100); setTimeout(function(){ console.log("222"); }, 250); };

点击后,控制台输出111,222这是毋庸置疑的,那么,“222”在点击多长时间之后输出呢?(忽略代码的运行时间) 是350ms ???其实是250ms。

在理解为什么之前首先要知道,在javascript中定时器是异步执行,异步???解释一下:

js是任务执行环境是单线程,为了解决单线程带来的麻烦,js将任务执行模式分为两种,同步和异步。同步是指任务在主线程上等待运行,而异步是指任务在任务队列中等待,主线程什么时候为空就执行任务队列。那么,什么样的任务是放在任务队列中的呢?定时器、ajax、各种事件(如点击、滑动等事件)~~ps:我只知道这些啦~~~~~

了解这些之后,再来说一下上面的答案为什么是250ms。

定时器最重要的就是记住,指定的时间间隔表示何时将定时器的代码添加到队列中,而不是何时实际执行代码。

因此,100ms时将第一个setTimeout的任务放入任务队列中,250ms时将第二个setTimeout任务放入任务队列中(注意这个250ms是相对于开始,即100ms之后的150ms)。所以,答案就是250ms啦!

再来说一下setInterval(重复定时器)

重复定时器有两个问题

1>某些间隔会被跳过

2>多个定时器的代码执行之间的间隔会比预期的小

举个例子,onclick事件处理程序使用setInterval()设置200ms的间隔,而事件处理需要300ms完成,来看一下这段代码的进程时间线

javascript定时器的那些事儿

从图中可以看出,5ms时创建了间隔200ms的定时器,205ms时定时器代码添加到队列中,但是,onclick事件处理程序需要300ms,定时器需要等待事件处理程序之后才可以执行(setTimeout也遵循这种方式,只不过为了说明前面的问题,所以忽略了事件处理程序的时间,前面也已经标注)因此,在300ms后才可以执行第一个定时器代码。可是,在405ms时又在队列中添加了另外一个副本,在605ms时,第一个定时器代码仍在运行,而且队列中还有一个定时器正在等待,所以此时的定时器代码不会被添加到队列中。

所以,我们在使用setInterval()时经常发现它计算的时间不太对,为了弥补这两个缺点可以使用setTimeout代替重复定时器。

setTimeout(function() {
    setTimeout(arguments.callee, interval);
}, interval);

每次函数执行时都会创建新的定时器,第二个定时器使用arguments.callee来获取当前的执行函数引用,并设置另一个定时器。这样做就可以在定时器代码执行之前不加入新的定时器代码,而且保证了下一次定时器代码执行之前,至少要等待指定的间隔。

利用setTimeout数组分块

想象一下这种情况,当处理一些数据时需要对大量的数据进行循环,可想而知,如果单纯用for循环这是一件非常可怕的事情。

解决办法:将处理项目创建一个队列,使用定时器取出一个要处理的数据,接着再处理另一个数据

/**
 * [chunk description]
 * @param  {[type]} array   [处理项目的数组]
 * @param  {[type]} process [处理项目的函数]
 * @param  {[type]} context [可选运行该函数的环境]
 * @return {[type]}         [description]
 */
function chunk(array, process, context) {
    setTimeout(function() { var item = array.shift(); process.call(context, item); if(array.length > 0) { setTimeout(arguments.callee, 100); } }, 100); } //处理的数组 var data = [100, 200, 600, 300, 245]; //处理的函数 function printVlaue(item) { console.log(item); } //调用数组分块函数 chunk(data, printVlaue);

但是,但是,利用这种方法你必须要考虑你处理的数据不需要同步处理并且不按顺序执行,毕竟setTimeout是异步处理。

还有一点还需注意,chunk函数里处理数据是用shift()函数,会改变原数组的值,因此,如果你还需使用原数组值时,最好将data数组克隆处理。

函数节流

我们知道js中DOM操作是从document开始查找,所以比非DOM操作需要更多的内存和cpu时间。想象一下,在随着调整窗口大小触发resize事件,并获取某一元素其高度也随之改变,浏览器需要不断计算其高度大小值,对于浏览器来说这是很大的工作量。

解决办法:节流

 

var processor = {
    tId: null, throttle: function(method, context) { clearTimeout(method.tId); method.tId = setTimeout(function() { method.call(context); }, 100); } }; window.onresize = function() { processor.throttle(resizeDiv); }; function resizeDiv() { var div = document.getElementById('myDiv'); div.style.height = div.offsetWidth + 'px'; console.log(div.style.height); }

 

首先在throttle方法中先清除之前设置的定时器,然后创建新的定时器,这样每隔100ms浏览器才计算一次高度,又不会影响视觉效果。也可以适当增加间隔毫秒数。

只要代码是周期性执行的,都可以使用节流。

 

嘿嘿~如果有什么不对的地方求指导~~~~~