setTimeout与setInterval的区别

2016年11月27日Web前端0

当我们遇到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函数,我们应该像开始那样用匿名函数来代替直接使用字符串的方式。