secrets of the javascript Ninja( javascript Timer)(javascript忍者的秘密)

javascript中定时器是如何工作的

     计时器(定时器)是一个大家常常不能够充分理解,并且常常被误用的JavaScript的特性之一,使用计时器能够开发非常复杂的动态效果,它具有在一段时间后异步执行一段代码的能力,由于JavaScript天生是单线程的,但是使用计时器能够打到异步执行一段代码的能力。

 

 

    从根本上理解定时器是如何工作的是非常重要的,由于它是单线程运作,定时器如何运作并不是太明了,下面我们通过一下三个方法,来看看它到底是如何执行的,这三个方法能够形成和操作计时器:

var id = setTimeout(fn, delay);该方法初始化一个单独的计时器,用于在delay指定的时间后调用fn函数,该方法会产生一个唯一的ID,稍后可以使用该ID取消该计时器

 

var id = setInterval(fn, delay); 该方法和上面的方法类似,他会在每隔delay指定的时间后都会都会执行fn函数,也会产生一个唯一的ID,用于取消定时器。

 

clearInterval(id);, clearTimout(id);接受上述两个函数返回的ID,停止定时器调用上面两个函数中的第一个参数。

 

 

     为了理解定义器内部是如何工作的,有一个重要的概念我们需要理解:定时器延迟时间并不能保证。因为所有的JavaScript在浏览器中执行一个单线程的异步事件时,只有当有一个入口时才能开始执行。

      通过下图,我们能够更好的理解这个原因:

 

       在这个示例中有很多信息可以挖掘,但是完全理解了之后你将会更清楚地认识到异步的JavaScript是怎么执行的。这是个一维的图:竖直方向上的是(挂钟式)时间,单位为毫秒。蓝色的框表示正在执行的JavaScript片段。举例来说,第一块JavaScript执行了约18ms,而鼠标点击则执行了约11ms,以此类推。

 

 

       由于JavaScript向来都只能在同一时间执行一块代码(这是由它单线程的本质决定的),所以每一个代码块都“阻塞”了其他的异步事件。这意味着当异步事件发生时(比如鼠标点击、timer触发或者是XMLHttpRequest完成),这些事件将进入到一个队列中等待执行(队列的实现方法因浏览器而异,我们在此只讨论一个简化的情况)。

 

      首先,在第一个JavaScript块中,有两个timer被初始化了:一个10ms的setTimeout和一个是10ms的setInterval。由于timer(这里的timer指setTimeout中的 timer,而下文中的interval则指setInvertal中的timer)开始的时间,实际上它在第一个代码块结束前就已经触发了。然而请注意,它并不会马上执行(事实上由于单线程的存在,它也无法做到马上执行)。相反的,这个被延期执行的函数进入队列中,等待在空闲的时候被执行。

 

     在第一个JavaScript块中,我们看到一个鼠标点击事件也发生了。而与这个异步事件(我们不知道用户什么时候会去执行一个动作,因此将其认为是一个异步动作)相关的JavaScript回调函数也无法立马执行,正如timer一样,它也进行到队列中等待被执行。

 

     当第一个JavaScript块被执行完之后,浏览器问了一个问题:有正在等待被执行的代码吗?在这个例子中,鼠标点击事件和time事件都正在队列中等待。于是浏览器选了一个(鼠

标点击事件),然后马上执行它。而timer只能继续等下去。

 

     注意当鼠标点击事件正在执行的时候第一次的interval事件也触发了,与timer一样,它的事件也进入队列等待之后执行。然而,注意,当interval再次触发的时候(这个时候timer的事件正在执行),这一次它的事件被丢弃了。如果你在一个大的JavaScript代码块正在执行的时候把所有的interval回调函数都囤起来的话,其结果就是在JavaScript代码块执行完了之后会有一堆的interval事件被执行,而执行过程中不会有间隔。因此,取代的作法是浏览器情愿先等一等,以确保在一个interval进入队列的时候队列中没有别的interval。

 

    事实上,我们可以在例子中看出:当第三个interval触发的时候这个interval自身正在执行。这告诉我们一个重要的事实:interval是不管当前在执行些什么的,在任何情况下它都会进入到队列中去,即使这样意味着每次回调之间的时间就不准确了。

 

 

    最后,当第二个interval回调执行完后,我们可以看到队列已经被清空,没有什么需要JavaScript引擎去执行的了。这表明浏览器现在等待一个新的异步事件发生。于是在50ms的

时候我们看到interval又触发了。这一样,由于没有什么东西挡住了它的执行,它马上就触发了。

 

   让我们来看一个例子,这个例子更好地阐释了setTimeout和setInveral之间的区别。

 

setTimeout(function(){
/* 一个很长的代码块…… */
setTimeout(arguments.callee, 10);
}, 10);

setInterval(function(){
/* 一个很长的代码块…… */
}, 10); 

 

     乍看上去,这两段代码在功能上似乎是相同的,可实际上并非如此。setTimeout的代码在前一次的回调执行完后总是至少会有10ms的延时(有可能会更多,但是绝对不会更少);而setInterval则总是在每10ms的时候尝试执行一次回调,它不管上一次回调是什么时候执行的。

      我们在此学到了很多,让我们重述一下:

    * JavaScript引擎只有一个线程,这使得异步事件必需列队等待执行。

    * setTimeout和setInterval在如何执行代码上有着本质地区别。

    * 如果一个timer在将要执行的时候被阻塞,它将会等待下一个时机(比预期的延时长)。

    * 如果interval的执行时间较长(比指定的延时长),那么它们将连续地执行而没有延时。

什么地方使用JavaScript定时器

  1. 大量处理dom时,我们可以采用分步加载的办法。

      我们通过下面的代码理解这种用法:

     var table = document.getElementsByTagName("tbody");
for ( var i = 0; i < 2000; i++ ) {
var tr = document.createElement("tr");
for ( var t = 0; t < 6; t++ ){
var td = document.createElement("td");
td.appendChild( document.createTextNode(" "+t ));
tr.appendChild( td );
}
table[0].appendChild( tr );
}
//如果我们采用分步加载呢;
var i = 0, max = 1999;
setTimeout(function(){
for ( var step = i + 500; i < step; i++ ) {
var tr = document.createElement("tr");
for ( var t = 0; t < 6; t++ ){
var td = document.createElement("td");
td.appendChild( document.createTextNode("" + t));
tr.appendChild( td );
}
table[0].appendChild( tr );
}
if ( i < max )
setTimeout( arguments.callee, 0 );
}, 0);
 

  1. 中央定时器控制

当大量使用定时器时,我们该如何管理这些定时器,这种情况在动画处理中尤其重要,因为需要同时处理大量的属性,所以需要一种方案管理这些。

var timers = {
timerID: 0,
timers: [],
start: function(){
if ( this.timerID )
return;
(function(){
for ( var i = 0; i < timers.timers.length; i++ )
if ( timers.timers[i]() === false ) {
timers.timers.splice(i, 1);
i--;
}
timers.timerID = setTimeout( arguments.callee, 100 );
})();
},

stop: function(){
clearTimeout( this.timerID );
this.timerID = 0;
},

add: function(fn){
this.timers.push( fn );
this.start();
}
}; 

 

通过使用这样一个定时器,我们可以了解到一下一个好处:

 

1.在同意时间一个页面上只有一个定时器在执行。

2.你可以根据你的意志暂停或者重新开始定时器。

3.你可以频繁的处理删除的回调函数。

下面来看看如果使用这个中央定时控制器:

<div id="box" style="position:absolute;" mce_style="position:absolute;">Hello!</div>
var d=document,box = d.getElementById("box"), left = 0, top = 20;
使用的时候只需要:
timers.add(function(){
box.style.left = left + "px";
if ( ++left > 50 )
return false;
});
timers.add(function(){
box.style.top = top + "px";
top += 2;
if ( top > 120 )
return false;
});
}; 

 

 

 

时间: 2024-12-19 16:21:49

secrets of the javascript Ninja( javascript Timer)(javascript忍者的秘密)的相关文章

secrets of the javascript Ninja (Function Prototypes)(javascript忍者的秘密)

       函数原型有很多用途,比如可以用来向一个函数实例上添加一些属性,但是它的一个最主要用途是使JavaScript能够以面向对象的方式编程. 实例化和原型(Instantiation 和 prototypes) 所有函数默认的都会有一个含有空对象的prototype属性,它的这个特性只有在实例化后才会有用,为了能够理解它的这种特性是多么的重要,需要知道一个重要的原则:函数具有双重功能,它可以作为一个平常的函数,也可以作为一个类. 我们可以通过下面一个小例子来看看如何使用prototype

secrets of the javascript Ninja(Function Type)(javascript忍者的秘密)

大多数情况下,typeof就可以完成检查函数类型的工作,例如: function ninja(){} alert(typeof ninja);  但是根据浏览器的不同,我们可以发现以下集中情况 在firefox2和firefox3中,如果检测一个<object/>类型的元素,将会检测出来是一个function <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.

JavaScript 小技巧之JavaScript基础

javascript|技巧 一.什么是JavaSCRIPT语言? JavaScript是一种新的描述语言,此一语言可以被箝入HTML的文件之中,透过JavaScript可以做到回应使用者的需求事件(如form的输入)而不用任何的网路来回传输资料,所以当一位使用者输入一项资料时,它不用经过传给服务器(server)处理,再传回来的过程,而直接可以被客户端(client)的应用程式所处理.你也可以想像成有一个可执行程式在你的客端上执行一样.目前已有一些写好的程式在Internet上你可以连过去看看.

发布一个高效的JavaScript分析、压缩工具 JavaScript Analyser_javascript技巧

发布一个高效的JavaScript分析.压缩工具 JavaScript Analyser 先发一段脚本压缩示例,展示一下JSA语法压缩和优化功能. 复制代码 代码如下: try {         //xxxx();     }     catch (e) {         yyyy();         function f1() {         }     }     finally {         zzzz();     }     function f2(var1) {    

利用javascript移动div层-javascript 拖动层_javascript技巧

利用javascript移动div层-javascript 拖动层: 程序功能:利用javascript开发在界面上随意拖动以下html code中的div层. javascript移动div层-javascript 拖动层代码-html code: 复制代码 代码如下: <div id="div_Info" style="display: none; dz-index: 101; left: 175px; width: 650px; position: absolut

JavaScript中伪协议 javascript:使用探讨_javascript技巧

将javascript代码添加到客户端的方法是把它放置在伪协议说明符javascript:后的URL中.这个特殊的协议类型声明了URL的主体是任意的javascript代码,它由javascript的解释器运行.如果javascript:URL中的javascript代码含有多个语句,必须使用分号将这些语句分隔开.这样的URL如下所示: javascript:var now = new Date(); "<h1>The time is:</h1>" + now;

secrets of the javascript Ninja( with(){} 的用法)(javascript忍者的秘密)

      with语句也是一个功能强大的特性,但是它常常不能被正确的理解.它允许你把一个对象的所有属性放到with语句所指定的作用域中,这样这些属性就可以想平常的JavaScript变量被使用.理解with语句是如何工作的,在你开发中会带来很大的好处.   JavaScript中with(){}语句是如何工作的 让我们首先通过一个小例子来看看with(){}语句的基本用法: var use = "other"; var katana = { isSharp: true, use: f

在JavaScript中使用timer示例_javascript技巧

复制代码 代码如下: function foo() { } setInterval( "foo()", 1000 ); 如果使用OO的技术,可以这样, 复制代码 代码如下: // constructor function MyObj { function foo() { alert( this.data ); } this.timer = foo; this.data = "Hello"; setInterval( "this.timer()",

使用 Chrome 开发工具调试异步 JavaScript(Debugging Asynchronous JavaScript with Chrome DevTools)

使用 Chrome 开发工具调试异步 JavaScript 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 终于又发现一个值得研究的领域,一头雾水,继续研究,估计研究明白了,也就能翻译明白了,也