JS定时器之杂谈

从一个经典的问题说起

1
2
3
4
5
6
7
function myTimeoutFunction()
{
doStuff();
setTimeout(myTimeoutFunction, 1000);
}

myTimeoutFunction();

1
2
3
4
5
6
7
function myTimeoutFunction()
{
doStuff();
}

myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

如下内容主要翻译,整理自johnresig,也就是jQuery之父这位大佬的博客。(大佬博客带飞)

这两个函数乍看之下是一样的,都是间隔1s,不断运行doStuff()这个函数,那有没有区别呢?这就是今天要说的两个函数

  • var id = setTimeout(fn, delay); 生成单次定时器,并在延时后调用fn这个函数
  • var id = setInterval(fn, delay); 每次间隔后都会调用fn这个函数,直到被取消。

有一句非常重要的知识点必须了解JS是单线程的,由于JS在浏览器中运行是单线程的,异步时间(比如click或者timer)只会在执行时运行。

点击查看大图
蓝色的框表示正在执行的JavaScript部分,例如,第一个JavaScript block执行大约18ms,Mouse clickblock 大约11ms,依此类推.

由于JS单线程特性(一次只能执行一段代码),这些代码块中的每一段都“阻塞”了其他异步事件的运行。换句话说,当一个异步事件发生时,比如(比如鼠标点击,计时器触发或者XMLHttpRequest完成),会在队列中等待执行。

首先,在第一个JS block中,2个定时器被初始化,一个 10ms setTimeout 和一个 10ms setInterval。由于Timer开始的时间和地点,在我们实际完成第一个JS代码块之前,实际上会被触发,但是注意!并不会立刻执行(由于单线程的原因)这是会进入队列排队,等待执行

此外,在第一个JS block中,我们看到一个鼠标点击事件的发生,与这个异步事件相关的JavaScript回调,和Timer类似,不会立即执行,也在队列中等待。

在第一个JS block初始化完成之后,浏览器会抛出一个问题“What is waiting to be executed?(哪个函数等在被执行)”,现在鼠标点击,和Timer都在等待,然后浏览器选择一个(鼠标点击回调)并立即执行。Timer将等待下一个可行的时间执行。

注意图上标注,当鼠标点击事件正在处理时,第一个interval也执行了(之前定义的定时器的第一次循环),和之前一样,它的处理程序也会排队等待。但是请注意,当这个interval再次被触发(此时定时器的处理程序正在运行),这次interval的handler就会被移除。浏览器如果不这样处理的话,就会有很多个interval的处理在排队。所以浏览器倾向简化队列,直到没有interval在队列中(多的都会被drop掉)。

这告诉我们一个事实: Interval 不关心 现在正在执行什么事件,他们只会接着在队列中排队,即时这意味着在两个callback之间的interval会被删除。

最后,在第二个时间间隔回调完成执行之后,我们可以看到JavaScript引擎没有什么可执行的了。这意味着浏览器现在等待发生新的异步事件。当间隔再次发生时,我们得到这个在50毫秒的标记。然而,这一次没有任何阻止它的执行,所以它立即启动。

setTimeout代码在上一次回调执行之后总是会有至少10ms的延迟(只会多,不会少),而setInterval不管最后一次回调执行的时间,将会每10ms执行一次回调。

这样刚开始的两个程序也就有答案了不是么~

(这篇翻译的不是很好,之后会修改润色一下。。)