定时器
JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成。它们向任务队列添加定时任务。
1,setTimeout()
setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。
var timerId = setTimeout(func|code,delay);
setTimeout函数接受两个参数,第一个参数是将要推迟执行的函数名或一段代码,第二个参数是推迟执行的毫秒数(如果省略默认为0)。
setTimeout函数还允许更多的参数,它们将以此传入推迟执行的函数(回调函数)。
setTimeout(function(a,b){
console.log(a + b);
},1000,1,1);
以上代码,setTimeout共有4个参数,最后两个参数将在1000毫秒之后回调函数执行时,作为回调函数的参数。
如果回调函数是对象的方法,那么setTimeout是的方法内部的this关键字指向全局环境,而不是定义时所在的那个对象。
2,setInterval()
setInterval函数的用法与setTimeout完全一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
var timer = setInterval(function(){
console.log(2);
},1000);
//每个1000毫秒就会输出一个2,知到窗口关闭
和setTimeout一样,除了前两个参数,可以接受更多的参数传入回调函数。
3,clearTimeout(),clearInterval()
setTimeout和setInterval函数都返回一个整数值,表示计数器编号。将改正数传入clearTimeout和clearInterval函数,就可以取消对应的定时器。
使用如下:
var timer1 = setTimeout(f,1000);
var timer2 = setInterval(f,1000);
clearTimeout(timer1);
clearInterval(timer2);
4,实例:debounce函数(防抖动)
如果,我们不希望回调函数被频繁调用。比如,用户填入网页输入框的内容,希望通过Ajax方法返回服务器,JQuery的写法如下:
$('textarea').on('keydown',ajaxAction);
这样写有一个很大的缺点,就是如果用户连续击键,就会触发keydown事件,造成大量的Ajax通信。这是不必要的,而且很可能产生性能问题。正确的做法应该是,设置一个门槛值,表示两次Ajax通信的最小间隔时间。如果在间隔时间内,发生的新的keydown事件,则不必触发Ajax通信,并且重新开始计时。如果过了指定时间,没有新的keydown事件,再将数据发送出去。
这种做法叫做debounce(防抖动) 。假定两次Ajax通信的时间间隔不得小于2500毫秒,上面的代码可以改写成下面这样:
$('textarea').on('keydown',debounce(ajaxAction,2500));
function debounce(fn,delay){
var timer = null; //声明计时器
return function(){
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context,args);
},delay);
};
};
上面代码中,只要在2500毫秒之内,用户再次点击,就会取消上一次的定时器,然后再新建一个定时器。这样就保证了回调函数之间的调用间隔,至少是2500毫秒。
5,强行机制
setTimeout和setInterval的运行机制,是将指定的代码移出本轮时间循环,等到下一轮循环,再检查是否到了指定时间。如果到了,就执行对应的代码,如果不到就继续等待。
这意味着,setTimeout和setInterval指定的回调函数,必须等到本轮事件循环的所有同步任务都执行完,才会开始执行。由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeout和setInterval指定的任务,一定会按照预定时间执行。
setTimeout(someTask,100);
veryLongTask();
上述代码setTimeout指定100毫秒以后运行一个任务,但是如果后面的veryLongTask函数(同步任务)运行时间非常长,过了100毫秒还无法结束,那么被推迟运行的someTask就只能等着,直到veryLongTask运行结束,才能轮到它执行。
setInterval(function(){
console.log(2);
},1000);
sleep(3000);
function(ms){
var start = Date.now();
while((Date.now() - start) < ms) {
}
}
上面代码中,setInterval要求每隔1000毫秒,就输出一个2.但是紧接着的sleep语句需要3000毫秒才能完成,那么setInterval就必须推迟到3000毫秒之后才能开始生效。生效后setInterval不会产生累计效应,即不会一下子输出三个2,而是只会输出一个2.