关于setTimeout和setInterval

给出代码

1
2
3
4
5
function print1(){
console.log(1);
};
setTimeout(print1,0);
console.log(2);

上面这段代码运行之后的结果会是多少呢?1和2到底是谁先输出来的?想不明?想不明白就对了,想明白了请你走开。结果应该是先输出2再输出1。

胡诌的理论

根据我了解的一些知识和测试的结果,我给出了自己的一个解释。首先,我们需要理解js运行代码的方式。在js的世界里,有两个非常重要的概念,一个是同步的代码,一个是异步的代码。当有同步的代码需要执行时,js就会优先处理同步代码。如果同步的代码都已经运行完毕,js就会检查一个我叫做异步队列的东西,里面存储了所有需要异步执行的回调函数。当js发现这个异步队列中有任务的时候就会取出并执行,直至完成所有异步队列中的任务。此后就进入等待状态,这个等待状态可能是一个死循环,不断检查异步队列并执行。还要补充一点,当有新的同步代码的时候,js会优先处理。

ok,有了前面的介绍,我们现在可以解释整个过程了。js开始执行同步代码,然后他发现下一条指令是setTimeout/setInterval,那么js就会开启一个计时器,然后继续执行后面的代码。我们接着来考虑计时器的事情,需要说明的是,这个计时器是独立工作的,已经和js没什么关系了。当计时完成的时候,程序就会把setTimeout/setInterval中定义的回调函数添加到异步队列的后面,到这里为止,就没计时器什么事情了。我们再回到js中去,当js完成了所有的同步代码之后就会检查异步队列。他发现了我们之前添加到异步队列中的函数!就会取出执行。

setInterval的特别之处

这里有一个特殊的情况需要说明,就是setInterval函数的工作方式有点独特。每当setInterval的计时器完成计时之后,他会检查异步队列中是否已经包含了之前添加的回调函数(就是说计时器之前添加的回调函数还没有执行,可能是因为同步代码过多),如果之前添加的回调函数确实还没有执行,新的回调函数就不会添加到异步队列中了。正是这个原因,你不能期待setInterval总是能发出足够数量的信号。验证这个想法的代码如下,输出结果应该为1、2、1、1、1…,而不是1、1、1…2、1、1…

1
2
3
4
setInterval(function(){console.log(1)},0);
var loop = 100000000;
while(loop--){}
setTimeout(function(){console.log(2)},0);

clearTimeout和clearInterval

经过测试可以确认一点,当我们调用clearTimeout/clearInterval时,不仅仅会停止计时器,还会直接把异步队列中的回调函数删除。

alert的影响

如果js调用了alert函数,那么计时器会被暂停计时。但是网上查到的资料里,有人说会重置计时器,我表示不相信。难道是浏览器兼容性导致的??管他呢。我要睡觉了。