文档章节

javascript计时器的工作原理解析

0000001
 0000001
发布于 2011/05/13 10:21
字数 2484
阅读 636
收藏 1
代码使用方法:

说明:这篇文章节选自John Resig 的《Secrets of the JavaScript Ninja》一书,本人翻译只是供大家学习,翻译不足之处,请斧正。

这篇文章主要从下面几个方面解读计时器:

计时器概述;
计时器速度深度探析;
用计时器处理大量任务;
利用计时器管理动画;
较好的计时器测试

计 时器是一个我们了解很少且经常被滥用的东西,它是javascript的特色。实际上,在复杂的应用程序开发中,它能为我们提供很多帮助。计时器提供了一 个可以将代码片段异步延时执行的能力,javascript生来是单线程的(在一定时间范围内仅一部分js代码能运行),计时器为我们提供了一种避开这种 限制的方法,从而开辟了另一条执行代码的蹊径。

有趣的是,与我们普遍接受的观点相反,计时器并不是javascript语言的一部分,而是浏览器引入的方法和对象的一部分。这意味着如果你选择在一个非浏览器的环境运行它,很有可能计时器不存在,你必须使用特定功能推行你自己的版本(如Rhino线程)。

1、计时器是如何工作的

从根本上来说,理解计时器如何工作很重要。通常情况下,计时器的行为并不直观,因为它在一个单独的线程中,让我们从三个函数的测试开始,对于每一个函数我们都有机会构建和控制计时器。

var id = setTimeout(fn,delay);启动一个计时器,它将在延迟时间之后调用特定的函数,该函数返回一个唯一的ID,利用这个ID计时器在稍后的时间里被取消;
var id = setInterval(fn,delay);与setTimeout相似,但它不断的调用函数(每隔一定延迟时间)直到它被取消;
clearInterval(id),clearTimeout(id);接受计时器的 ID(由上述任意一个函数返回)并停止调用计时器。

为了理解计时器内部是如何工作的,有一个很重要的概念需要加以探讨:延迟是无法保证的。既然浏览器中所有javascript 是在一个单线程中运行的,那么异步事件(如鼠标点击、计时器)在执行中也只有存在开放状态时才运行,下面这张图很好的说明了这个问题:



这张图有很多信息需要消化,充分理解它将使你对异步js执行有一个更好的认识,图表是一维的,在垂直方向上是时间(挂钟),以毫秒为单位。蓝色盒子代表js执行的比例。例如,第一个javascript块运行时间大约为18秒,鼠标点击大约为11秒等等。


既 然javascript在一定时间内之执行一部分代码(源于单线程的特性),那么这些代码块的每一个就被封锁在其它异步事件执行的进程中。这表明当一个异 步事件发生时(如鼠标点击、计时器释放、XMLHttpRequest请求完成),它将排队等候执行(如何排队在不同浏览器之间是不一样的)。

首 先,在第一代码块里,有两个计时器触发:一个是10ms的setTimeout,一个是10ms的setInterval。在第一个代码块真正完成之前, 它实际上已经释放了。但是,注意,它不会立即执行(由于单线程的问题,它无能为力),相反,为了能在下一个可行的时间得到执行,那些延时函数被编入队列。

另外,在第一个代码块内,我们看到鼠标点击出现。与这个异步事件(我们永远不知道何时执行动作,这样就可以认为它是不同步的)相关Javascript回调函数跟初始的计时器一样不能立即被执行,它排队等候执行。

在Javascript最初的代码块执行完毕之后,浏览器会发出疑问:正在等候执行的是什么?在这种情况下,鼠标点击处理器和计时器回调函数同时处于等待之中,然后,浏览器将选择一个并立即执行它,计时器函数将等到下一个可能的时间执行。

注 意,当鼠标点击函数处理器执行时,第一个回调函数也在执行,至于计时器,其处理器被编入队列稍后执行。但是,请注意,当Interval再次释放时(在计 时器处理器执行时),计时器执行的时间将减少。如果你在一大块代码执行期间将所有的Interval回调函数编入队列,其结果是一大群Interval回 调函数会毫无延迟的执行,直到全部完成。而浏览器在队列增大之前只是简单的等到没有Interval处理器排队为止(间歇问题)。

事实上,我们看到这样一个情况:Interval正在执行时,第三个Interval函数将释放。这表明一个重要的事实:Interval对当前正在执行什么漠不关心,它们将不会青红皂白的排队,即使是牺牲回调函数之间的时间也在所不辞。

最后,在第二个Interval函数执行完毕之后,我们可以看到没有留下任何Javascript引擎执行的东西。也就是说,浏览器在等待一个新的异步事件的出现。Interval再次释放时,我们在50ms处获得它,但这一次,执行起来没有任何障碍,它立即释放。

我们来看一个例子,以便更好的说明setTimeout和setInterval的差异:

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

乍一看,这两段代码似乎功能相同,但并非如此。setTimeout代码在前一个回调函数执行万之后,至少有10ms的延迟(最终可能多些,但至少不会少于此),而setInterval将每隔10ms尝试执行一次回调函数而不管最后一个回调函数何时执行。

这里有很多我们需要了解,让我们回顾一下:

Javascript只有单线程,异步事件被迫排队等候执行;
setTimeout和setInterval在如何执行异步代码方面有根本的区别;
如果计时器无法立即执行,它将延时到下一个可能的时间执行(这比预想的延迟时间要长一些);
如果有充分的执行时间,Interval可能会毫无延迟的来回执行。
所有这些无疑是重要的知识,了解Javascript引擎如何工作,特别是有大量异步事件出现时,这使得在构建高级应用代码片段时有一个良好的基础。

2、计时器最小延时时间和可靠性

很明显,你可以延迟几秒钟、几分钟、几小时或任何你你想要的时间间隔,但最不明显的是你能选择的最小延时时间。

在一定程度上,浏览器不能为计时器提供良好的解决方案用以精确的处理它们(因为它们自身受操作系统时间的限制)。但是,纵观所有的浏览器,可以很安全的说,最小延时时间大约是10-15ms。

我们可以对跨平台假定的计时器间歇作简单的分析后得出这一结论。例如,如果我们分析延迟时间为0ms的setInterval,我们会发现在大多数浏览器中的最小延迟时间。

在OS操作系统下的浏览器中:

从左上角开始,依次为:Firefox 2, Safari 3, Firefox 3, Opera 9



在Windows操作系统下得浏览器中:

从左上角开始依次为:Firefox 2, Internet Explorer 6, Firefox 3, Opera 9
setTimeout(function(){
/* Some long block of code... */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code... */
}, 10);


上面图表中的线条和数字显示了浏览器同时处理时间间歇的数量,我们可以得出结论:在OS上,浏览器的最小延时时间为10ms,在windows上为15ms。我们可以通过为计时器提供0(或任何10ms以下的任何数值)作为延时时间得到这个值。

但 有一个例外,IE为setInterval提供德尔延时时间不能为0(即使setTimeou能欣然的接受)。当setInterval的延时时间为0 时,它会转变成setTimeout(仅执行一次回调函数),而我们可以通过为其提供1ms的延迟来解决这个问题。由于所有浏览器都能自动向上舍入任何低 于最小延时时间的值,所以用1ms与有效的使用0ms一样安全,或更安全(既然IE浏览器现在能工作)。

从这些表中我们可以得到其它信 息。最重要的是加强了我们以前所了解到的:浏览器不能保证你所指定的精确的时间间歇。像Firefox 2,Opera 9(OS)在提供可靠的执行率方面有一定的难度。很多与浏览器如何处理Javascript的垃圾回收有关(Firefox 3在Javascript的执行上作了显著的改善,其垃圾回收在这些结果中立竿见影)。

因此,浏览器可以提供非常小的延迟时间,但其精确度得不到保证,那么在使用计时器时,你需要考虑你的应用程序(如果10ms和15ms有差异,你应该重新思考你应用程序代码的结构)。

© 著作权归作者所有

0000001
粉丝 34
博文 141
码字总数 46256
作品 0
朝阳
前端工程师
私信 提问
JavaScript的计时器的工作原理

最近都在看一些JavaScript原理层面的文章,恰巧看到了jQuery的作者的一篇关于JavaScript计时器原理的解析,于是诚惶诚恐地决定把原文翻译成中文,一来是为了和大家分享,二来是为了加深自己对...

第三方支付接口
2014/11/30
58
0
javascript计时器原理

昨天在w3c上学javascript的计时器的时候,写了个计时程序。 源码很简单: 运行的时候,我突然发现,多次点击开始计时的话,每点一次,就会多一个计时器,时间增加的越来越快。 原以为是启动了...

cassia_
2015/07/07
117
2
javascript引擎执行的过程的理解--执行阶段

一、概述 js引擎执行过程主要分为三个阶段,分别是语法分析,预编译和执行阶段,上篇文章我们介绍了语法分析和预编译阶段,那么我们先做个简单概括,如下: 1、语法分析: 分别对加载完成的代...

saucxs
05/23
0
0
JavaScript的工作原理:解析、抽象语法树(AST)+ 提升编译速度5个技巧

摘要: JS的"编译原理"。 原文:JavaScript的工作原理:解析、抽象语法树(AST)+ 提升编译速度5个技巧 作者:前端小智 Fundebug经授权转载,版权归原作者所有。 这是专门探索 JavaScript 及...

Fundebug
01/22
0
0
【译】JavaScript的工作原理:事件循环及异步编程的出现和 5 种更好的 async/await 编程方式

此篇是JavaScript的工作原理的第四篇,其它三篇可以看这里: 【译】JavaScript的工作原理:引擎,运行时和调用堆栈的概述 【译】JavaScript的工作原理:V8引擎内部+关于如何编写优化代码的5...

妙堂传道者
01/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

打印城市名字

题目要求 写一个脚本让用户输入多个城市的名字(可以是中文),要求不少于5个,然后把这些城市存到一个数组里,>最后用for循环把它们打印出来。 参考答案 #!/bin/bash#这个脚本用来打印城市...

wzb88
28分钟前
3
0
Prometheus基于DNS注册

1 搭建dns主从 1)环境 环境10.0.2.49 主10.0.2.50 从 2)安装dns yum install bind-chroot 3)配置主服务器 10.0.2.49 vim /etc/named.confoptions { listen-on port 53 { ...

拜了个拜
31分钟前
5
0
从0搭建自己的webpack开发环境(三)

往期回顾: 从0搭建自己的webpack开发环境(一) 从0搭建自己的webpack开发环境(二) 1.处理JS模块 1.1 将es6代码编译成es5代码 代码的转化工作要交给babel来处理 npm install @babel/core ...

前端优选
31分钟前
5
0
menu和actionbar

3.0之后推荐用actionbar,Menu已经过时但是我们还是了解一下吧, Menu学习 在java语句中添加menu组件 重写onCreateOptionsMenu(Menu menu)方法 @Override public boolean onCreateOptio...

逆天游云
39分钟前
5
0
目前流行前端几大UI框架

在前端项目开发过程中,总是会引入一些UI框架,已为方便自己的使用,很多大公司都有自己的一套UI框架,下面就是最近经常使用并且很流行的UI框架。 一.Mint UI 屏幕快照 2019-01-18 下午3.03....

说雾
39分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部