JS 中的计时器
更新日期:
JS 的计时器
JS 是单线程语言,但它允许通过设置 “超时值” 和 “间歇时间值” 来调度代码在特定的时刻执行。
- 超时调用:window.setTimeout() 在指定的时间过后执行代码
- 间歇调用:window.setInterval() 每隔指定的时间间隔重复执行代码,直到间歇调用被取消or页面被卸载
它两都是 window 对象的方法,所以 this 的值…需要注意哦~
JavaScript 的核心是 ECMAScript,而在 Web 中使用 JS,真正的核心无疑是 BOM(浏览器对象模型)。BOM 的核心是 window,它表示浏览器的一个实例。在浏览器中,window 对象扮演以下两种角色:
1.通过 JS 访问浏览器窗口的一个接口
2.ECMAScript 规定的 Global 对象:在网页中定义的任何一个对象、变量、函数,都以 window 作为其全局对象,也就有权访问 parseInt()、setTimeout()、setInterval() 等方法了
超时调用 setTimeout()
以下代码会在1秒后显示一个警告框。
|
|
第一个参数:要执行的代码
可以是字符串,比如”alert(‘Hello world’)”,就和 eval() 函数里使用的字符串一样(但传递字符串有性能损失,故不建议用)
也可以是函数(建议用 function,即如上)
第二个参数:表示等待多长时间的毫秒数,即经过多长时间把当前任务添加到任务队列中去。
如果队列是空,那添加的代码就会立即执行;如果队列不为空,那就等等前面的代码执行完了之后才能执行。所以说,这里设置的该事件后指定的代码不一定会执行,只能更长,不会更短。
返回值:一个数值 ID,表示超时调用,这个 ID 是计划执行代码的唯一标识符
可以通过它来取消调用,clearTimeout(timeoutId); 只要是在指定的时间尚未执行之前取消调用,那就可以完全取消超时调用。【那如果是已经在任务队列里排队待执行嘞?记得 jQuery 动画的 stop() 方法就有专门的参数问是否要清空任务队列里的。Try下~】
间歇调用 setInterval()
间歇调用与超时调用类似,参数与之相同;间歇调用ID;取消间隔调用 clearInterval(intervalId);
|
|
区别
|
|
以上这段代码看起来功能是等价的,但事实却不是的。
setTimeout 的代码会在每次回调函数执行之后至少需要延时10ms才执行(只可能更多,但不会少)。但是 setInterval 会每隔 10ms 就去尝试执行一次回调函数,而不管上一个回调函数是不是还在执行。它并不关心当前谁在执行,它的回调函数会不加区分地进入队列。
执行异步代码时,setTimeout 和 setInterval 是有本质区别的。如果 interval 回调函数执行需要很长时间(比指定的延时长),那 interval 有可能会没有延时地一个接一个地被执行。
最好不用间歇调用,原因:
1.取消调用(非循环一直调用时):
(1)取消间隔调用的重要性要远远高于取消超时调用,因为在不加干涉的情况下,间歇调用将会一直执行到页面卸载。
(2)在使用超时调用时,没必要跟踪超时调用ID。因为每次执行代码后,如果不另外设置,调用会自行停止。
2.一般认为,使用超时调用来模拟间歇调用是一种最佳模式。因为后一个间歇调用可能会在前一个间歇调用结束之前启动,而超时调用则可以完全避免这一点。所以在开发环境下,很少使用真正的间歇调用。
JS 计时器的原理
由于 JS 是一个单线程的解释器,所以,它一定时间内只能执行一段代码。它有一个 JS 的任务队列来专门控制要执行的代码。这些任务会按照将它们添加到队列的顺序来执行(据说排队和调用的方式会依据浏览器的不同而不同,此处简化为直接排在最后-且一一调用)
所以,事实上,设置的计时器的延时是没有保证的。所有在浏览器中执行的 JS 单线程异步事件(比如计时器、鼠标点击事件、XMLHttpRequest请求完成)都只有在它有空的时候才会执行。
看下这张图,源自JavaScript的计时器的工作原理:
垂直方向是以毫秒计的时间;蓝色的块代表了当前正在执行的 JS 代码段。
把上图翻译成代码模拟下,代码摘自JavaScript的计时器的工作原理,并对其进行了些修改。
|
|
计时器在哪初始化,就在哪开始计时。在 Chrome 中的运行结果如下:
按照上面的原理来解释下运行结果:
- 一开始设定的超时调用并不是在10ms后立即执行,而是被添加到了任务队列的后面。等待第一阶段执行完成后才执行的,距离初始化时的时间也不是10ms
- 鼠标点击事件是在第一阶段代码执行期间被触发的,它没有等待第一阶段执行完毕,而是被高优执行了(在Chrome中测试。或许是浏览器自己做的优化吧,以便及时响应用户的交互)
- 最后执行间歇调用,它也不是严格按照10ms的间隔执行的。
参考
JavaScript的计时器的工作原理: http://segmentfault.com/a/1190000002633108
《JavaScript高级程序设计(第3版)》 中的第8章: 8.1.6 间歇调用和超时调用