本文来自 @xiaoyuze88 连接:http://xiaoyuze88.github.io/javascript
过久没碰代码了,那天想到关于循环调用setTimeout
实现每隔一秒输出递增的数的那个问题,搞了搞,发现不少概念模糊了,在此总结下。java
所谓的循环调用setTimeout
实现递增输出,就是说用for
循环10次,每隔一秒输出一个从0~9的数。git
很少说,直接上最终代码再说,细节后面再谈。github
for (var i = 0; i < 10; i++) { //这里用闭包,为每个i生成一个独立的上下文环境,传递给里面的console.log,而不会受到setTimeout延时而影响 (function (i) { `setTimeout`(function () { console.log(i); }, 1000 * i) })(i); }
这里主要的问题在:ajax
setTimeout
等函数的工做机制。首先闭包,这里就很少说了,在这里,闭包的做用就是给闭包内的函数生成一个不受外面环境干扰的上下文环境,因为js的做用域问题。浏览器
若是这里不用闭包,写成诸如:闭包
for (var i = 0; i < 10; i++) { `setTimeout`(function () { console.log(i); }, i * 1000); }
会发现,每隔一秒钟,输出一个10。异步
这是因为这一个for循环的执行,瞬间就完成了,也就是说,瞬间注册了10个延时执行的函数,每个隔一秒钟执行。函数
当注册的时间点到来,开始执行setTimeout
中的语句,因为定义域的问题,此时console.log(i)
的这个i指向的是已经到达10的for循环中的i,这就是为何要用闭包来给setTimeout
设置独立的上下文环境,而避免须要访问i时访问到了外面的变量。线程
另外,若是细想一下,会发现setTimeout
的工做过程多少让人有点迷惑,到底setTimeout
等延时类函数在浏览器中是如何运做的?这就牵扯到下一个问题,关于浏览器中是若是运做的问题。
浏览器中,JS引擎是单线程的,假设一个浏览器中有三个常驻线程,既JS引擎线程、渲染线程、事件触发线程,还有处理完即结束的线程如AJAX异步请求。
其中,JS线程与渲染线程是互斥的,这是为了不JS控制DOM时与页面渲染发生冲突。而对于JS线程,它是由事件驱动的,因为单线程,全部任务依队列排序。若是页面上触发了事件,如onclick=function(){}
、或者由setTimeout
添加了一个函数、ajax
请求返回的事件等,全部新添加的任务位于队尾等待处理。
因为是单线程,若是线程被阻塞,如while(true){}
死循环,则一切新添加的任务都将被阻塞。
由上面所述,就能够理解为何setTimeout
或setInterval设置的延时事件并非真是函数处理的延时时间,既setTimout(code,1000)并非必定会在1秒后处理,这段代码发生的仅仅是在1秒后,将待处理函数排与js任务队列末尾。