对setTimeout函数的理解

之前去面试一家公司时,面试官出了一道关于js的setTimeout函数的题目:

 1 /*
 2 *面试官给的原题目如下:
 3 *执行mytest()后,控制台输出内容是_____
 4 *function mytest() {
 5 *    for(var i = 0; i < 5; i ++)
 6 *        setTimeout(console.log(i),0);
 7 *}
 8 *但这应该不是面试官的问题,当时我也没有发现问题,回来测试后才发现,这个函数不是
 9 *面试官要表达的意思。
10 */
11 //正确的代码如下:
12 function mytest() {
13     for(var i = 0; i < 5; i ++)
14         setTimeout(function(){
15             console.log(i);
16         },0);
17 }

我的回答是:控制台输出为5 5 5 5 5,虽然答案对了,但是解释就太牵强了,我说是因为for语句之执行速度比setTimeout函数快,面试笑了笑,呜呜~~(这有毛关系)。
后来网上找了一些资料,参考了一些书籍,这里我给出一个靠谱的解释:

首先,我们必须承认,js是单线程的,即使是对于ajax异步方式或者像setTimeout这样的函数。

其次,我们要理解js函数的执行过程,对于setTimeout这样的函数来说并不是每次都能按照预定延迟的时间执行指定函数的。下面举一个列子:

比如有一个函数fun0在执行开始时创建了一个定时器T1,T1定时器将在200ms后被触发指定函数。这时我们需要考虑一个问题,假设fun0的执行时间为250ms(大于200ms),那将会怎样?前面已经说过,js是单线程的,所以不存在fun0和定时器同时执行的情况。这时候定时器制定的函数会在fun0执行完后才执行,定时器的等待时间为250ms,并不是我们指定的200ms。

对于javascript这样的执行方式,我们可以想象在函数执行过程中有两个队列。队列Q1是指执行队列,每次只能执行一个函数;队列Q2就是等待队列,存放着将要执行的函数。每当有一个函数要执行时,就会先把这个函数放进等待队列,如果Q1为空,那么久立即执行这个函数。当然在大多数情况下,函数都是立即执行的。

因此,我们可以知道,setTimeout定时器指定的函数必须要在当前执行队列为空时才会执行。

现在我们再来分析一下上面那道题,很显然,每次的for循环都触发了一个定时函数,这些定时函数有点特殊,是立即执行的(如果执行队列为空的话)。但是在每次触发时,for循环都还未结束,也就是执行队列不为空,此时新建的定是函数只能放在等待队列里无法立即执行。当最后一次for循环执行结束后,执行队列变为空,这时等待队列的函数就立即进入到了执行队列,于是就开始执行只控制台输出。因为setTimeout指定的匿名函数中i的值是一种引用值(自行谷歌脑补),所以输出结果为5 5 5 5 5。

如描述或理解有误,欢迎各位留言!!

posted @ 2014-10-19 13:16  进击的小矮人  阅读(3145)  评论(41编辑  收藏  举报