从一个基础知识角度出发,理解Javascript 定时器是如何工作的是十分重要的。由于在单线程中,定时器的行为表现的并不直观,甚至有时候会让你觉得很怪异,以下三种方式可以让我们去创建并操作定时器:

  1. var id = setTimeout(function,delay);
  2. var id = setInterval(function,delay);
  3. clearInterval(id);, clearTimeout(id);

   想要理解定时器是怎么工作的,怎么运行的,有一个概念十分重要,那就是:”Timer delay is not guaranteed”,(定时器的延时,并不是可靠的)。这是因为在浏览器在执行js代码的时候是在单线程中执行的,异步事件(比如鼠标时间和定时器)只有在处于运行期,才能够被执行。让我们来看这样一幅图.

实例

左侧为正常时序,右侧表示定时器的注册顺序和发生顺序。 这副图的左侧同时提出了很多问题,并给出了响应的答案,如果能够理解这幅图,对你理解js异步执行会有很大帮助,其实这是一张立体图,在垂直方向我们有一个时钟(左侧的间隔),单位为毫秒。中间蓝色的盒子,表示正在被执行的javascript代码块.例如第一个Javacript代码块执行消耗了大约18毫秒,鼠标事件所消耗的时间大约为11毫秒。 因为JavaScript在同一时间只能执行一段代码块,(主要是由于JS是单线程的)所以这些代码块将会阻塞其他的异步事件被执行。 这就意味着,当一个异步事件发生时(比如:一个鼠标事件,一个定时器触发,或者一个XHR请求完成时),他就会被插入事件队列排队等待执行(有一点很重要,在不同的浏览器中,这个队列模型是不同的,所以队列中的事件是如何触发的是不同的)。 首先: 在第一段js代码块中,两个定时器被初始化,一个setTimeout 和 一个setInterval。这个定时器启动实际上实在我们第一个js代码块完成之前,不过请注意,定时器所挂载的处理逻辑并没有立即被执行(由于线程模型是不能这样做的),而实际上,延时调用程序将会被插入队列,等待可调用时序时,被顺序执行。 其次: 我们在第一个代码块中,我们触发了一次点击操作。这个异步事件相关的回调函数,和定时器一样,也不会立即被执行,同样进入队列等待执行。 当第一个Javascript代码块执行完成后,浏览器就会去问队列:接下来要执行什么?然而此时此刻,鼠标事件的句柄函数和定时器的延时调用函数都在等待。浏览器会在二者中选择一个(鼠标事件)立即执行。定时器的回调会等待下个时机,被按顺序调用。 注意图中,在鼠标事件的回掉执行时,interval延时回掉被执行了。但是需要注意的时,当interval再次被出发时(当一个定时器的延时处理在执行的时候),这时候程序的处理将会被丢弃。假设当有大块的代码正在执行时,你又有一堆的interval延时调用在排队,你希望结果很可能就是这个大块的js代码执行完毕后,interval的延时调用会一个接一个的被触发,而且在执行时没有延时时间,也就是会被连续的调用。可是相反,浏览器往往只是等待,直到没有更多的interval处理程序进行排队。 事实上,我们也可以看到,第三个interval回掉触发的时候,这个interval本身也在执行中。这就像我们展示了一个很重要的现象就是:interval 并不在乎当前谁正在执行,他们不分青红皂白地将排队,即使这意味着回调之间的时间将被牺牲。 最后,当第二个interval回掉执行完成后,我们能看到,对于js引擎来说,没有需要去执行的东东了。这就意味着,浏览器在等待新的异步事件发生了。到第50秒时,这个interval被再次触发,这时候没有东西在阻塞执行,因此他会被立即调用。 我们来用几行代码来更好的去分辨setInterval和setTimeout之间的区别,

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

这两段代码乍一看似乎差不多,但事实上相差很多。有一点值得注意的是,在这里面的setTimeout,两个回掉执行的时间间隔至少会是10毫秒;而setInterval将尝试每10秒去执行一次,不去考虑上一次回掉是否已经完成。

我们回顾一些要点:

1.Javascript是一个单线程执行的东东,迫使异步事件排队等待执行。

2.setTimeout 与 setInterval执行代码的原理是完全不同的。

3.当一个定时器执行被阻塞时,他会等待下一个可能执行的时机去执行,所以这个延时可能会比预先设定的时间要长。

4.如果回调函数执行时间过长(长于定时器的延迟时间),“间隔定时器”有可能会一个接一个无间隔的执行

2013 10 17 补充 补充一个典型的例子


	var die = false;
	setTimeout(function() {
    		die = true;
	}, 100);
	while(!die) {
	}
	console.log('done');
	

如上代码按照正常逻辑 初始变量 die 的值为false,如果你没有看懂此篇文章,你一定会觉得在100毫秒后,die的值变成true,然后console会被执行,如果你这样想那你就错了。记住setTimeout的准则是尽快执行,而不是立即执行。只有当主事件循环结束是,有时间片供setTimeout去执行时,定时器才会被执行。