今天刷牛客网上的一道题
for(var i=0;i<5;++i){
setTimeout(function(){
console.log(i+ ’ ’);
},100);
}
问运行结果是什么。
之前我想当然的认为肯定是0,1,2,3,4啊,然而真的不是想当然的。这里考察的是setTimeout的异步运行。
这段程序可以理解为:匿名函数根本就没有立即执行。正因为没有立即执行,所以在循环的过程中,匿名函数没有及时访问到每一个变量,这样外部函数循环完成之后,匿名函数才执行开始访问外部函数的变量,而这时变量的值早已成为最后一个。所以最后的结果是5,5,5,5,5。
想要出现0,1,2,3,4,很简单,给匿名函数传入参数即可。
for(var i=0;i<5;++i){
setTimeout(function(){
console.log(i+ ’ ’);
}(i),100);
}
直接是()也行……
for(var i=0;i<5;++i){
setTimeout(function(){
console.log(i+ ’ ’);
}(),100);
}
结果都可以,于是我就想啊,这个setTimeOut到底是怎么个运行机制呢?
众所周知,setTimeout定时器时用来产生异步执行的效果的,也就是异步事件。
js是单线程的语言,如果前一个任务耗时很长,后一个任务就不得不一直等着。有时候,前一个任务的cpu是在空闲着,这时想要充分利用cpu缩短任务执行时间,可以
挂起处于等待中的任务,先运行排在后面的任务。等到前一个任务返回了结果,再回过头,把挂起的任务继续执行下去。
这样,js可以分为主线程和任务队列,用来完成异步任务。
具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
欢迎各位大牛批评指正……(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(taskqueue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
任务队列,是个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。
当主线程上的任务都执行完了,就从任务队列中依次取事件来执行。
上述题是的var i变量在主线程上,所以会先执行了i,等i都执行完之后,才会将任务队列中的回调函数取出来执行,所以最后的结果是5,5,5,5,5,。
加上参数的意思是给这个回调函数传入参数,相当于形成了块级作用域,使得匿名函数能在for这个块级中自己执行,所以能在for循环的时候执行,也就是说能在循环的同时传入参数,结果是0,1,2,3,4。(其实还是有点不太理解………………)
参考文章:http://blog.csdn.net/qingwenxiutong/article/details/52397676