首先来看setTimeout()的基本用法:
setTimeout(function(){
console.log("ok!");
},1000)
参数1:要延迟执行的代码,也可以不选择。
参数2:延迟多少秒后执行。(其实这种说法是不准确的,实质是在一段时间后将当前任务加入到队列当中去)
上面代码的表现:在1秒以后将alert。
那么再看下面的代码
var start = new Date();
setTimeout(function(){
var end = new Date();
console.log("Time elapsed: ", end - start, "ms");
}, 500);
while (new Date - start <= 1000)
{
}
运行这段脚本可以看到:Time elapsed的值大概在1001ms左右,肯定会超过1000ms。也就是说:setTimeout失效了,指定的函数并没有在500ms后执行,而是延迟到1000ms后才执行。加入再把while中的内容改为new Date()-start>0,则会陷入死循环中,不会执行setTimeout里的函数。
再看下一段代码
function a()
{
setTimeout(function(){console.log(1);},0);
console.log(2);
}
a();
运行这段脚本可以看到:先打印2后打印1,我们在setTimeout里面指定了0ms,希望能立即执行,但是实际上没有效果。
再来一个典型例子
var j = 0;
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(j);
console.log(i);
},0);
j++;
}
输出:0 5 1 5 2 5 3 5 4 5。可以发现虽然延迟设为了0,但是不代表就会立即执行。setTimeout里的函数是在for循环完了以后才入栈的
原因:除了js是单线程执行这一点以外,对于javascript还维护着一个setTimeout队列,未执行的setTimeout任务就按出现的顺序放到setTimeout队列,等待普通的任务队列中的任务执行完才开始按顺序执行积累在setTimeout中的任务。就是说script脚本加载完成了之后,setTimeout才会执行注册的函数。
JavaScript是单线程执行的,无法同时执行多段代码。当某一段代码正在执行的时候,所有后续的任务都必须等待,为了控制要执行的代码,就有了一个javascript的队列。这些任务会按照将他们添加到队列的顺序执行。而setTimeout()的第二个参数就是告诉javascript再过多长时间把当前任务(参数一)添加到队列中。注意:
如果队列是空的,那么添加的代码就会立即执行。
如果队列不是空的,那么就要等前面的代码执行完以后再执行。
也就是说setTimeout只能保证在指定的时间过后将任务(需要执行的函数)插入队列等候,并不保证这个任务在什么时候执行。执行javascript的线程会在空闲的时候,自行从队列中取出任务然后执行它。javascript通过这种队列机制,给我们制造一个异步执行的假象。