两个都是定时函数,setTimeout()只执行一次,setInterval()按时间段循环执行。
现在有这样一个问题,在函数中递归调用setTimeout()可以达到和setInterval()一样的效果,如下:
1. function test() { setTimeout(test, 10); //do something } 2. function test(){...} setInterval(test, 10);
看起来效果一样,但是John Resig告诉我们由于setInterval()的实现机制,导致上面两种方法大有区别:
原文 http://ejohn.org/blog/how-javascript-timers-work/
文章大意是,对函数func(),时间长度t::
setInterval()这样处理可能产生一个问题,当func()因为某些原因而延迟执行时,可能导致多个func()在短时间内执行多次。这可能由于func()自身执行时间过长,或者是q中其它函数竞争导致func()一直无法执行。这显然属于逻辑出了问题。
实验:
1 function test() { 2 var t = new Date(); 3 console.log("start: "+ t.getSeconds() + "." + t.getMilliseconds()); 4 5 var times = 0; 6 while((new Date() - t)<3000) { 7 times++; 8 } 9 10 t = new Date(); 11 console.log("end: "+ t.getSeconds() + "." + t.getMilliseconds()); 12 } 13 14 var id = setInterval(test,2000);
每个test()执行时间为3s,每隔2s执行一次test()。那么1个test()还没有执行完第2个test()就已经加入到q中了。实验结果如下:
可以看到test()函数连续执行,没有像预期一样每隔2s执行一次。
而 setTimeout() 的处理方式就不会产生这种问题,由于setTimeout()每次都是基于当前时间,而且必须是在func()得到执行时才会产生下一次执行机会。则当CPU繁忙时其执行次数大大小于setInterval()方式。因此setTimeout()方式有在有助于缓解CPU与内存的压力,但是如果我们对func()的执行次数有要求还是需要使用setInterval(),因为该方法不会因为函数延迟执行导致执行次数减少。
clearTimeout 与 clearInterval
两者根据func()的注册id来取消尚未执行的函数,动作是将其从q中移除。但是两者都无法阻止正在执行的func(),此时func()会正常执行到结束。对于setInterval()这种情况很好处理,但是对于setTimeout()可能导致其clear不成功。
由于setTimeout()每次都会产生一个新的id,又由于单线程的并发性,因此导致了清理的不确定性。如下:
1 var test = function() { 2 //do something 3 id = setTimeout(test, 1000); //put at top to clear the next func 4 //do something 5 }
id保存最新的待清理函数标识,假设此时id为20。当编号为20的test()执行到第2行时,如果此时我们执行了clearTimeout(20),那么由于其代表的test()正在执行,相当于什么也没有做。而紧接着的第3行代码注册了一个标识为21的待执行函数,而此时clearTimeout()已经执行过了,清理失败。因此为了保证当前的id永远代表下一个待执行函数的标识,应该将行3的代码提升至函数顶,如下:
var test = function() { id = setTimeout(test, 1000); //put at top to clear the next func //do something }
此时只要test得到执行,id立刻更新为下一个函数的id。也就杜绝了清理失败的可能。
原文:http://www.cnblogs.com/defghy/p/3560895.html