读了《javascript 忍者秘籍》后对定时器的一些心得。
定时器提供了一种让一段代码在一定毫秒之后,在异步执行的能力。(html5 web worker 对定时器做了很多改变,IE不支持)
定时器有一个很重要的概念 javascript定时器的延迟时间是不确定的
在web worker可用之前,浏览器中的所有javascript代码都是在单线程中运行的。这种情况下,异步事件的处理程序,如用户界面事件和定时器,在线程中没有代码执行时才进行执行。也就是说,处理程序在执行时必须进行排队执行,并且一个处理程序不能中断另一个处理程序的执行。
定时器对队列的工作方式是,当特定的时间过去后,将代码插入,注意添加到队列并不
意味着它会马上执行,而只能说它会尽快执行。对于定时器来说,指定的时间间隔表示何
时将定时器的代码添加到队列中,而不是何时执行代码。
举例来说,设定一个250ms后执行的定时器,不代表250ms后它会马上执行,它只会表示在250ms后被加入到队列中,如果这个时间点队列是空闲的,那么这段代码就会被执行。
浏览器内核处理方式
浏览器内核实现允许多个线程异步执行,这些线程在内核制控下相互配合以保持同步.假如某一浏览器内核的实现至少有三个常驻线程:javascript引擎线程,界面渲染线程,浏览器事件触发线程,除些以外,也有一些执行完就终止的线程,如Http请求线程,这些异步线程都会产生不同的异步事件,下面通过一个图来阐明单线程的JavaScript引擎与另外那些线程是怎样互动通信的.虽然每个浏览器内核实现细节不同,但这其中的调用原理都是大同小异.

由图可看出,浏览器中的JavaScript引擎是基于事件驱动的,事件驱动一般通过事件循环(event loop)和事件队列(event queue)来实现的.这里的事件可看作是浏览器派给它的各种任务,从代码角度看来任务实体就是各种回调函数,JavaScript引擎一直等待着任务队列中任务的到来.由于单线程关系,这些任务得进行排队,一个接着一个被引擎处理
+ GUI渲染线程
该线程负责渲染浏览器界面HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行.该线程与JavaScript引擎线程是互斥的,这容易理解,因为JavaScript脚本是可操纵DOM元素,在修改这些元素属性同时渲染界面,那么渲染线程前后获得的元素数据就可能不一致了.
在JavaScript引擎运行脚本期间,浏览器渲染线程都是处于挂起状态的,也就是说被”冻结”了.所以,在脚本中执行对界面进行更新操作,如添加结点,删除结点或改变结点的外观等更新并不会立即体现出来,这些操作将保存在一个队列中,待JavaScript引擎空闲时才有机会渲染出来.
+ GUI事件触发线程:
JavaScript脚本的执行不影响html元素事件的触发,在t1时间段内,首先是用户点击了一个鼠标键,点击被浏览器事件触发线程捕捉后形成一个鼠标点击事件,由图可知,对于JavaScript引擎线程来说,这事件是由其它线程异步传到任务队列尾的,由于引擎正在处理t1时的任务,这个鼠标点击事件正在等待处理.
+ 定时触发线程:
注意这里的浏览器模型定时计数器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的,如果处于阻塞线程状态就计不了时,它必须依赖外部来计时并触发定时,所以队列中的定时事件也是异步事件.
setTimeout & set Interval
`
setTimeout(function(){
…
setTimeout(repeat,10);
},10);
setInterval(function(){
...
},10);
`
这两段代码,功能看似相同,但实际上不是。setTimeout,要在前一个callback执行结束后并延迟10s,才再次执行。而setInterval则是每隔10s就尝试执行callback回调,而不关注上一个callback何时执行。
所以 setTimeout & set Interval在触发周期上的定义是完全不同的。
setInterval创建的定时器确保了定时器规则地插入队列中,这个方式问题在于(1)某些间隔会被跳过了(2)多个定时器的代码执行间隔可能会比预期的要小 (3)如果一直被延迟,可能发生定时器无延迟执行,并且同一个interval处理程序的多个实例不能同时进行排队。
定时器的用处
- 计算密集型的代码,将代码的各个部分分解成不会让浏览器挂掉的碎片
- 中央定时器控制
- 异步测试
参考:《javascript 忍者秘籍》
http://www.laruence.com/2009/09/23/1089.html