
			<p>看下面内容之前，看一小段代码，如果读者能说出代码的用意，那就没必要往下看了，因为你都懂。</p>
<blockquote><p>setTimeout(function(){</p>
<p>/* Some long block of code&#8230; */</p>
<p>setTimeout(arguments.callee, 10);</p>
<p>}, 10);</p>
<p>setInterval(function(){</p>
<p>/* Some long block of code&#8230; */</p>
<p>}, 10);</p></blockquote>
<p>计时器是一个很牛X的东西，但是很多人其实只限于知道它的语法，缺乏对其原理的认识。计时器通过设定一定的时间段（毫秒）来异步的执行一段代码。因为 Javascript 是一个单线程语言，计时器提供了一种绕过这种语言限制来执行代码的能力。</p>
<p>今天就简单的来说下计时器的工作原理。</p>
<p>JavaScript 提供了三个函数来构建和操作计时器</p>
<p>1 var id = setTimeout(fn, delay);</p>
<p>2 var id = setInterval(fn, delay);</p>
<p>3 clearInterval(id); clearTimeout(id);</p>
<p>具体的语法我就不多说了，可以查手册。为了了解计时器的工作原理，有一个概念必须记在心里：时间延迟不能被保证。什么意思，就是说你这样写setTimeout(fn, 500)并不代表fn肯定在500毫秒之后马上就执行，延迟很可能会更长。因为 JavaScript 是单线程语言，所有的异步事件（包括计时器、鼠标事件或者一个 XMLHttpRequest 完成）仅仅当程序执行期间有缺口的时候才会执行，不是你规定了什么时候就什么时候执行，要知道程序员不是万能的，你写的东西最终还是要看浏览器脸色的。</p>
<p>下面的这张图片可以很好的说明问题，感谢 John Resig 大神。</p>
<p><img class="aligncenter size-full wp-image-1116" title="Timers" src="http://www.daqianduan.com/wp-content/uploads/2010/05/Timers.png" alt="" width="427" height="320" /></p>
<p>从上往下看，左面的数字代表时间（毫秒），右面的文字代表了一系列异步事件的设置和触发，中间则是代码块。最上面的 JavaScript 代码块可能是你在浏览器载入的时候执行的片段，大概耗时18毫秒，紧接着下面的 Mouse Click Callback 代码块可能是你一个鼠标事件触发时的回调函数，大概耗时11毫秒，依次类推。</p>
<p>JavaScript 的单线程特性决定了每次只能执行一块，所以当第一块代码执行的时候（它一共运行了18毫秒），本身构造了两个计时器，期间可能用户还点了一下鼠标（你有过在网页一打开还没载完就在那乱点的情况吗）。按理说用户点完鼠标就应该马上执行那个回调函数，但是不行，JavaScript 执行只有一条道嘛，在那18毫秒没跑完之前，其他代码块想执行就只能排队，没空间给你超车都。那两个计时器都是10毫秒的延迟，从图中可以看到，setTimeout也在那18毫秒执行结束之前触发了，没办法也排队吧。</p>
<p>终于，18毫秒后，天上一道神雷把前面的车直接劈成空气了，后面两个排队的可以过去了，但是还得一个一个，不能并列，那谁先过去呢？是不是两个人在那划拳？不是的，浏览器说的算，浏览器说，鼠标单击事件先过去，setTimeout只能继续等11毫秒。注意看图，在鼠标事件回调函数执行的时候，又一个计时器事件触发了（setInterval），等着，而且必须排在setTimeout的后面。</p>
<p>11毫秒过去了，setTimeout 终于可以过去了，注意看，setInterval 的第二次触发了，虽然它第一次都在排队呢，如果这个时候还向往常一样排队，最后是什么情况，setTimeout执行完了，就会连续执行两个setInterval，你设置的延迟没用了都。所以浏览器还是比较智能的，它在处理setInterval的时候，如果发现已经有排队的，就直接把新来的 Kill 掉。</p>
<p>接着看，轮到排队的 setInterval 第一次触发开始执行了，它执行的时候，第三次触发又到了，这一次没有排队了，所以浏览器没把它 Kill 掉，给丫排队的机会，所以你会发现这两次的setInterval的执行没有间隔的，如果你做一个幻灯片，遇到这种情况就要好好想想自己的代码是不是有问题了。</p>
<p>最后，再也没有别的因素干扰了 setInterval 了（假如用户被 MM 叫走了），setInterval 就按照你想要的步骤执行了。</p>
<p>讲到这里，开头的代码可以理解了吧。</p>
<blockquote><p>setTimeout(function(){</p>
<p>/* Some long block of code&#8230; */</p>
<p>setTimeout(arguments.callee, 10);</p>
<p>}, 10);</p>
<p>setInterval(function(){</p>
<p>/* Some long block of code&#8230; */</p>
<p>}, 10);</p></blockquote>
<p>这两个函数看起来效果一样，其实不然，第一个代码块总会延迟10毫秒执行，虽然大多时候是大于10毫秒的。而第二个每到10毫秒就尝试执行，不管之前的触发执行了没有。</p>
<p>总结起来四条</p>
<p>? JavaScript 引擎只有一个线程，它会迫使某些异步事件排队</p>
<p>? setTimeout 和 setInterval 在执行异步代码的时候有很大区别</p>
<p>? 假如一个计时器被阻止执行，它会等待知道遇到一个代码执行空隙，通常时间比预计的要长</p>
<p>? Intervals 可能会一个挨着一个执行，如果回调函数的执行时间大于间隔</p>
<p>转载请注明：<a href="#">大前端</a> &raquo; <a href="#">一家之言：说说 JavaScript 计时器的工作原理</a></p>		