作为入门者来说,了解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/
JavaScript中timer是如何工作的