setTimeout的运行机制
先看下面一个例子:
<script type="text/javascript">
var t=true;
window.setTimeout(function(){
t=false;
},1000);
while(t){
}
alert('end');
</script>
这个代码会让浏览器陷入崩溃,为什么?
其实:
当javascript运行时遇到setTimeout后其实会另开一条线程(刚刚不是说javascript是单线程的嘛?)。是的,没错javascript是单线程的,但是setTimeout会另开一个线程,这个线程依赖于某个计时装置,这时,javascript其实是不管setTimeout而继续往下进行的,而setTimeout也是独立的线程计时的。而javascript在运行完全部的代码后会回过头来重新检测自己的队列中有没待执行的命令或者函数,如果有就执行,没有就等待。直到有待执行的命令被加载进这个队列中然后执行。在这期间,setTimeout依赖的那个计时线程如果时间到了,那么它会把setTimeout中要执行的函数或者命令传送到javascript的等待队列中,等待javascript执行完命令后回头来检测等待队列中未执行的函数或命令。
所以,在这里案例中,首先t变成true,然后javascript往下执行,遇到了setTimeout,这时setTimeout启用了另一个计时线程。此时javascript不管它跳过(因为它由另一个线程来控制)。然后遇到了while(t)这个循环,因为前面把t设置成了真,所以这个循环一直成立,所以也就一直循环下去。javascript的线程一直结束不了,所以无法回来检查等待队列,根本不会执行setTimeout中代码。所以t永远也无法变成假,而javascript也永远无法停止。
setTimeout的执行顺序
了解了setTimeout的运行机制后 我们来看看几个setTimeout并列运行 它们的执行顺序由什么决定
setTimeout(function(){
console.log(3);
},5000);
setTimeout(function(){
console.log(5);
},3000);
setTimeout(function(){
console.log(6);
},14000);
结果是5,3,6。几个都是setTimeout,因此都会放到等待队列~~~
而这些队列里的函数谁先执行呢?就是根据setTimeout里的第二个参数(延迟时间)决定的。
再看看一个复杂的例子 如果setTimeout中嵌套了setTimeout 呢?
var d=[];
d[0]=function(){setTimeout(function(){console.log(3);setTimeout(function(){console.log(4)})})};
d[1]=function(){setTimeout(function(){console.log(5);setTimeout(function(){console.log(6)},0)},100)};
for(var i=0;i<2;i++){d[i]();}
这段代码 按顺序打印的是 3,4,5,6 实际上是这样 主程序执行完后 遇到了两个settimeout函数 第一个是0毫秒,第二是100毫秒 先执行第一个settimeout,打印出3,
第一个函数执行的时候又遇见了一个settimeout 加入队列 等待时间是0,打印出了4,登到了100毫秒后 第二个settimeout被执行 打印出5.又遇见一个settimeout,同样被加入队列 等了0毫秒 被执行
再换换数据试一试:
var d=[];d[0]=function(){setTimeout(function(){console.log(3);setTimeout(function(){console.log(4)})})};
d[1]=function(){setTimeout(function(){console.log(5);setTimeout(function(){console.log(6)})},2)};
for(var i=0;i<2;i++){d[i]();}
这里的时间相差极短 打印出的顺序是3,5,4,6
如果再换一下数据:
var d=[];d[0]=function(){setTimeout(function(){console.log(3);setTimeout(function(){console.log(4)})})};
d[1]=function(){setTimeout(function(){console.log(5);setTimeout(function(){console.log(6)})},4)};
for(var i=0;i<2;i++){d[i]();}
这里相比上面的案例只是改了一个数字 2毫秒改成了4毫秒 执行顺序变成 3,4,5,6
为什么?
因为上面的案例执行完第一个settimeout之后 过了2毫秒了 超过了第二个函数的等待时间 会立即执行第二个settimeout。如果等待时间过大 就不会这样了
总之,只需记住一点:延迟时间始终是相对主程序执行完毕的那个时间算的 ,并且多个setTimeout的先后顺序也是由这个延迟时间决定的,如果遇到某个setTimeout需要花费大量的时间怎么办?可由于js是单线程,所以当执行到这个setTimeout后,会将这个程序执行完成后再去执行下一个setTimeout,无论下一个setTimeout的延迟时间为多少,如果这两个setTimeout时间的差值小于第一个setTimeout消耗的时间,程序会等待这个setTimeout执行完成后立即执行下一个setTimeout,如果差值大于消耗的时间,就按照和主程序约定的延迟(setTimeout里的第二个参数)执行即可