JavaScript中的Timer是怎么工做的

做为入门者来讲,了解JavaScript中timer的工做方式是很重要的。一般它们的表现行为并非那么地直观,而这是由于它们都处在一个单一线程中。让咱们先来看一看三个用来建立以及操做timer的函数。浏览器

  • var id = setTimeout(fn, delay); - 初始化一个单一的timer,这个timer将会在必定延时后去调用指定的函数。这个函数(setTimeout)将返回一个惟一的ID,咱们能够经过这个ID来取消timer。
  • var id = setInterval(fn, delay); - 与setTimeout相似,只不过它会持续地调用指定的函数(每次都有一个延时),直到timer被取消为止。
  • clearInterval(id);, clearTimeout(id); - 接受一个timer的ID(由上述的两个函数返回的),而且中止timer的回调事件。

要搞明白timer在内部是怎么工做的,咱们还须要知道一个很重要的概念:timer的延时并非每次都能如你所愿的。因为在同一个浏览器中全部的JavaScript都只在单一线程中执行,那些异步的事件(好比说鼠标点击,或者timer)只在执行期出现空闲的时候才会运行。这个用图最能表示清楚了,请参见下图:异步


(点击查看大图)

在这个示例中有不少信息能够挖掘,可是彻底理解了以后你将会更清楚地认识到异步的JavaScript是怎么执行的。这是个一维的图:竖直方向上的是(挂钟式)时间,单位为毫秒。蓝色的框表示正在执行的JavaScript片断。举例来讲,第一块JavaScript执行了约18ms,而鼠标点击则执行了约11ms,以此类推。函数

因为JavaScript向来都只能在同一时间执行一块代码(这是由它单线程的本质决定的),因此每个代码块都“阻塞”了其余的异步事件。这意味着当异步事件发生时(好比鼠标点击、timer触发或者是XMLHttpRequest完成),这些事件将进入到一个队列中等待执行(队列的实现方法因浏览器而异,咱们在此只讨论一个简化的状况)。post

刚开始,在第一个JavaScript块中,有两个timer被初始化了:一个10ms的setTimeout和一个是10ms的setInterval。因为timer(这里的timer指setTimeout中的timer,而下文中的interval则指setInvertal中的timer)开始的时间,实际上它在第一个代码块结束前就已经触发了。然而请注意,它并不会立刻执行(事实上因为单线程的存在,它也没法作到立刻执行)。相反的,这个被延期执行的函数进入队列中,等待在空闲的时候被执行。spa

另外,在第一个JavaScript块中,咱们看到一个鼠标点击事件也发生了。而与这个异步事件(咱们不知道用户何时会去执行一个动做,所以将其认为是一个异步动做)相关的JavaScript回调函数也没法立马执行,正如timer同样,它也进行到队列中等待被执行。线程

当第一个JavaScript块被执行完以后,浏览器问了一个问题:有正在等待被执行的代码吗?在这个例子中,鼠标点击事件和time事件都正在队列中等待。因而浏览器选了一个(鼠标点击事件),而后立刻执行它。而timer只能继续等下去。code

注意当鼠标点击事件正在执行的时候第一次的interval事件也触发了,与timer同样,它的事件也进入队列等待以后执行。然而,注意,当interval再次触发的时候(这个时候timer的事件正在执行),这一次它的事件被丢弃了。若是你在一个大的JavaScript代码块正在执行的时候把全部的interval回调函数都囤起来的话,其结果就是在JavaScript代码块执行完了以后会有一堆的interval事件被执行,而执行过程当中不会有间隔。所以,取代的做法是浏览器情愿先等一等,以确保在一个interval进入队列的时候队列中没有别的interval。队列

事实上,咱们能够在例子中看出:当第三个interval触发的时候这个interval自身正在执行。这告诉咱们一个重要的事实:interval是无论当前在执行些什么的,在任何状况下它都会进入到队列中去,即便这样意味着每次回调之间的时间就不许确了。事件

最后,当第二个interval回调执行完后,咱们能够看到队列已经被清空,没有什么须要JavaScript引擎去执行的了。这代表浏览器如今等待一个新的异步事件发生。因而在50ms的时候咱们看到interval又触发了。这同样,因为没有什么东西挡住了它的执行,它立刻就触发了。ip

让咱们来看一个例子,这个例子更好地阐释了setTimeout和setInveral之间的区别。

  setTimeout ( function ( ) {
    /* 一个很长的代码块…… */
    setTimeout (arguments. callee, 10 );
  }, 10 );
 
  setInterval ( function ( ) {
    /* 一个很长的代码块…… */
  }, 10 );

乍看上去,这两段代码在功能上彷佛是相同的,可实际上并不是如此。setTimeout的代码在前一次的回调执行完后老是至少会有10ms的延时(有可能会更多,可是绝对不会更少);而setInterval则老是在每10ms的时候尝试执行一次回调,它无论上一次回调是何时执行的。

咱们在此学到了不少,让咱们重述一下:

  • JavaScript引擎只有一个线程,这使得异步事件必需列队等待执行。
  • setTimeout和setInterval在如何执行代码上有着本质地区别。
  • 若是一个timer在将要执行的时候被阻塞,它将会等待下一个时机(比预期的延时要长)。
  • 若是interval的执行时间较长(比指定的延时长),那么它们将连续地执行而没有延时。

以上这些知识是至关重要的。知道JavaScript引擎的工做方式,尤为是知道它在有不少异步事件发生的时候是怎么工做的,为咱们在写进阶的应用程序代码打下了坚实的基础。

相关文章
相关标签/搜索