setTimeout与setInterval的区别
当我们遇到canvas动画,或需要延迟执行某段脚本等等时,我们经常会需要用到定时器。
setTimeout
基本的用法:
setTimeout(function(){
console.log(1);
}, 100);
以上代码会在在0.1s后在控制台上输出个1。function中的内容只会执行一次,当然setTimeout会返回个ID,配合clearTimeout就可以取消执行。
setInterval
与setTimeout类似,不过function中的内容会多次执行,
setInterval(function(){
console.log(1);
}, 100);
这段代码会每隔0.1s就在控制台上输出1,同样可以用clearInterval来取消执行。
存在的问题
延迟执行的时间,并不是完全按照setTimeout中的设置的时间的,比如我们设置100ms后执行console.log,但是就在第99ms时,突然有个任务占用了51ms,那么console.log就延迟执行了150ms,与设置的并不相同。
当天,setInterval也是不准的,他存在更为严重的问题,
var flag = 0;
var id = setInterval(function() {
for(var i = 0; i < 100000000; ++i) {
//...
}
flag++;
}, 10);
我们在页面刷新后,等待4s后,在控制台下输入clearInterval(id)(手工方法,再用setTimeout会不准,但不影响最后结果),然后,我们查看flag的大小,发现是39。而按照4 * 1000 / 10 应该是执行400次的,而这少了很多次。
假如,我们第0ms开始执行function中的内容,可是这么长的for循环要占用很长的时间,这段代码并不能在10ms时执行完成,当然在第10s时,定时器向执行队列中又插入这么一段代码。可是第一段的代码在20ms也没能执行完成,下面就有问题了,由于代码执行队列中已经有了一份未执行的代码了,所以在这个插入点(第20ms)时,会跳过,最后,整个代码只执行了39次。
所以,我们可以用递归的setTimeout来代替setInterval函数,这样就能保证在前一个定时器未执行完成之前,不会向队列中插入新的定时器代码,确保无缺失时间,避免连续运行。
避免使用eval
setTimeout('console.log(1)', 10);
setTimeout('write()', 10); // 注:此处调用的write函数是全局函数
function write() {
console.log(1)
}
以上用法也能达到正常的效果,但是这用到了JS中不被提倡用的eval函数,我们应该像开始那样用匿名函数来代替直接使用字符串的方式。