首页 > 代码库 > JavaScript中timer是如何工作的

JavaScript中timer是如何工作的


    作为入门者来说,了解JavaScript中timer的工作方式是很重要的。通常它们的表现行为并不是那么地直观,这是因为它们都处在单线程中。让我们先来看看三个用来出创建和操作timer的函数。

var id = setTimeout(fn, delay);

    初始化这个timer,然后这个timer将会在delay延时后调用这个函数fn。这个函数将返回一个唯一的ID,可以通过这个ID来取消timer。


var id = setInterval(fn, delay);

    与setTimeout类似,不过它会持续调用函数fn(每隔delay毫秒),直到timer被取消。

    

clearTimeout(id);
clertInterval(id);

    接受一个timer的ID,并停止timer的回调事件。


    为了理解timer内部工作原理,我们还需要知道一个重要的概念:timer的延时是不准确的。由于浏览器中的JavaScript在单线程中运行,因此异步事件(如mouse click以及timer)只有在执行期空闲的时候才运行。这个用图表最能解释清楚了,参看下图:

                             

    这个图标中有大量的信息可以挖掘,但是完全理解它将使我们对JavaScript中异步事件执行原理有个更深的认识。这是一张一维图:数值方向为时间,单位毫秒。蓝色的框表示正在执行的JavaScript片段。举个例子,第一块JavaScript大约执行了18ms,鼠标点击则执行了11ms,以此类推。

    由于JavaScript在同一时间只能执行一段代码(由单线程的本质决定),因此每个代码块都会“阻塞”其他异步事件。这意味着当异步事件发生时(比如鼠标点击、timer触发或者XMLHttpRequest完成),将进入事件队列等待执行(队列的实现方式因浏览器而异,这里只讨论简化的情况)。

    从第一段JavaScript代码块开始,有两个timer被初始化了:一个10ms的setTimeout和一个10ms的setInterval。其实在第一段代码块执行完之前timer就已经触发了。注意,无论如何它都不会立即执行(由于单线程原因,它无法那样做)。相反,这个延时执行的函数进入队列等待下次空闲的时候执行。

    此外,第一段代码块中鼠标点击事件也发生了。因此和异步事件相关联的回调函数也不能立即执行,像最初的timer一样,也要进入队列等待执行。

    最初的代码块执行完以后,浏览器问了这样一个问题:谁在等待执行?这个例子中,鼠标点击事件和timer回调函数都处于等待中。浏览器挑了鼠标点击事件,然后立即执行它。而timer将继续等待下次执行。

    注意当鼠标点击事件执行过程中时,第一次的interval的回调也触发了。和timer一样,它的事件也将进入队列等待执行。然后,当interval再次触发的时候(此时timer正在执行),这次它的事件将被丢弃。如果你在一大块代码执行时,把所有的interval的回调事件排入队列,其结果是在上述代码执行完以后,一推的interval事件将毫无间隔地执行。而浏览器更趋向于等待,以确保interval事件进入队列时没有其他的interval事件。

    事实上,从这个例子中可以看出:当第三个interval触发的时候,interval自身正在执行。这告诉我们一个重要的事实:interval不管当前执行的是什么,都会进入队列,即使这样意味着回调函数之间的时间就不准确了。

    最后,当第二个interval事件执行完以后,将没有任何代码留给JavaScript引擎执行。这意味着现在浏览器将等待新的异步事件触发。于是在50ms的时候,interval再一次触发。这一次,没有什么阻塞它的执行,它就立即触发了。

    让我们看一个例子来更好的阐述setTimeout和setInterval之间的区别。

    

setTimeout(function(){
    /*Some long block of code */
    setTimeout(argument.callee, 10)
}, 10);

setInterval(function(){
    /*Some long block of code */
}, 10)

    咋看一下,这两段代码的功能似乎是一样的,但实际上并非如此。尤其是setTimeout这段代码,在前一个回调函数执行完以后,都至少会有10ms的延时(可能会多,但绝不会少)。然而,setInterval的代码则总是每10ms的时候尝试执行回调,无论上一次回调什么时候执行的。

    重述下学到的知识。

  •  JavaScript引擎只有一个线程,这使得异步事件必须排入队列等待执行

  •  setTimeout和setInterval在执行异步代码上有着本质的区别

  •  如果timer在将要执行的时候被阻塞,它将等待下一个时机

  •  如果interval执行的时候很长(比指定的时间长),那么它们将连续地执行而没有延时。

    


原文:jQuery之父John Resig的Blog 

http://ejohn.org/blog/how-javascript-timers-work/