[ Javascript ] JavaScript中的定时器(Timer) 是如何工作的!

作为入门者来说,了解JavaScript中timer的工作方式是很重要的。通常它们的表现行为并不是那么地直观,而这是因为它们都处在一个单一线程中。让我们先来看一看三个用来创建以及操作timer的函数。

var id = setTimeout(fn, delay); 

- 初始化一个单一的timer,这个timer将会在一定延时后去调用指定的函数。这个函数(setTimeout)将返回一个唯一的ID,我们可以通过这个ID来取消timer。

var id = setInterval(fn, delay);

- 与setTimeout类似,只不过它会持续地调用指定的函数(每次都有一个延时),直到timer被取消为止。

clearInterval(id);, clearTimeout(id);

- 接受一个timer的ID(由上述的两个函数返回的),并且停止timer的回调事件。

要弄明白这个定时器内部是怎么工作的,有一个很重要的概念需要被提出来:

1 定时器延迟是不准确的(guaranteed).因为所有的javascript 浏览器雄代码只有一个单线程去执行,并且那些异步事件(如鼠标点击事件,和定时器)只会当出现线程空闲的时候去会执行。这有一个图表的演示,如下:

这里有很多信息在这个图中需要去理解,但是完全理解它之后,你会对javascript中异步机制有一个清楚的认识。这个图是一维的:

垂直方向我们用毫秒单位来标记这个时间,这个蓝色的方块代表这个正在被执行的javascript代码。例如,这第一个被执行的javascript代码大约花了18毫秒,这个鼠标事件块大约话费了11毫秒,等等。

由于javascript 引擎永远只会执行一个片段的代码在同一个时间(因为这个单线程机制),那么这时每一个代码块将会阻塞(blocking)别的异步事件的运行。

这意味着当一个异步事件被调用(例如当一个鼠标点击,一个定时器触发firing,或者一个xmlhttprequest 过程完成),它将会被加入到队里,并延迟执行(至于具体如何被入到队列中,不同的浏览器有不同的实现,我们这里只考虑简单的情况)

从一开始,在第一个javasript中,有两个定时器被初始化了: 一个10 毫秒的 setTimeout时间和一个10 毫秒的setInterval事件(这里注意,仅仅只是初始化,抑或叫作定义)。

由于这个定时器开始的时间和位置,导致它们在第一个javascript块完成前就已经真正被调用(这里的调用,并非直接执行,这里需要注意,可以理解为只是准备调用,把该回调方法加入到队列)了。注意,无论怎么样(however),定时器都不会立刻执行(因为线程没有空闲的原因,它没办法直接执行)。相反,这个被延迟的方法会被加入到队列中,在某个可以执行的时刻(线程空闲的时刻)执行。

另外一点,在第一个javascript块中,我们可以看到还有一个鼠标时间被触发了。这个javascript 回调方法被关联到一个异步事件 (没人知道用户什么时间做这个动作,所以它被认为是异步的),这个异步事件也不会立刻执行,和上面的定时器一样,也会被加入到队列中。

在第一个javascript 块执行结束之后,javascript 引擎就会立刻问一个问题: 还有什么在等待被执行的代码么? 那么这个时间,有一个鼠标事件回调和定时器回调同时在等待。这个浏览器马上挑选一个(从图中看,是鼠标事件回调)立刻执行。这个定时器继续等待,知道下一个可能的时刻。

注意一点:在这个这个鼠标事件处理函数正在被执行的同时,第一个interval 回调函数也会调用。正如前面提到的定时器一样,它的回调方法会被加入到队列中。然而,注意当这个interval再一次被调用(这个时候这个定时器的回调方法正在被执行),那么这个时候,这个interval 的回调方法将会被删除(drop)。 如果由于主线程需要执行很长时间的代码块,导致你在队列中加入了很多个回调方法,那么当这个主线程结束之后,一连串的回调函数连续执行没有间隔,直到结束。比较好的做法,是暂时让浏览器休息等待一会,让队列中没有Interval回调。

我们在看到一些情况:在第三个interval 回调方法触发的时候,inteval自身正在执行(这里应该是下在执行第二个interval没有结束)。这里给我们展示了一个重要的信息:

interval 不会去关心当前的线程现在执行什么,它们会把自己的回调方地加入到队列中在任何情况下,即使它会让两个间隔的回调方法之间的时间减少。

最后,在第二个interval(图中应该是第三个,这里应为中间有一个被drop掉了)被 执行完之后,javasript引擎中已经没有东西可以用来执行了。这也就是说,浏览器现在正在等一个新的异步事件需要去触发(occur)。在第50毫秒的时候,再一次触发了inteval回调。这个时候,已经没有东西去阻塞它的执行,所以它加入到队列中之后就立刻执行了。

接下来,让我们看一个例子更好的理解setTimeout与setInterval的区别:

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

  setInterval(function(){
    /* Some long block of code... */
  }, 10);

这两段代码可能在功能的实现上非常的相似,不经意一看,他们是完全一样的。尤其是这个setTimeout代码会在上一个回调函数执行之后至少隔10毫秒再执行一次回调方法(它可能会超过10毫秒,但不会少于10毫秒)。但是setInteval 却会尝试10毫秒就执行一个回调函数,不会去管上一个回调是什么时候执行的。

These two pieces of code may appear to be functionally equivalent, at first glance, but they are not. Notably the setTimeout code will always have at least a 10ms delay after the previous callback execution (it may end up being more, but never less) whereas
the setInterval will attempt to execute a callback every 10ms regardless of when the last callback was executed.

这里有一些东西是我们从这里学到的,做一个总结:

1 javascript引擎仅仅只有一个单线程,正在执行的异步事件会加入到队列等待。

2 setTimeout与setInterval 是执行异步回调方法从根本上不一样的。

3 如果一个需要立即执行的定时器被阻塞了,它竟无法被延迟执行,知道下一次线程空闲(那么被延迟的时间让会超过定时器定义的时间)

4 interval 可能会没有延迟的连续执行回调方法,如果主线程了执行一个足够长的代码(比定时的延迟长)

所有的这些都是非常重要的知识对于了解javascript引擎是如何工作的。特别是对于大数量的回调事件发生的时候,为我们建立更好的应用代码建立好的基础。

----------------------------------------------------------------------------------------------------

原文出自jQuery的作者John Resig。

地址:http://ejohn.org/blog/how-javascript-timers-work/#postcomment

时间: 2024-07-30 10:18:23

[ Javascript ] JavaScript中的定时器(Timer) 是如何工作的!的相关文章

Java中的定时器Timer

java.util.Timer是一个实用工具类,该类用来调度一个线程,使它可以在将来某一时刻执行. Java的Timer类可以调度一个任务运行一次,或定期运行. java.util.TimerTask是一个抽象类,它实现了Runnable接口.我们需要扩展该类以便创建自己的TimerTask,这个TimerTask内部使用java Timer类,可以被调度. Timer类是线程安全的,多进程不需要外部同步机制就可以共享同一个Timer对象.Timer类使用java.util.TaskQueue在

java中的定时器——Timer

一.类概述 Timer是一种定时器工具,用来在一个后台线程计划执行指定任务.它可以计划执行一个任务一次或反复多次. TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务. 二.代码实例 import java.util.Timer; import java.util.TimerTask; public class Reminder ...{ Timer timer; public Reminder(int seconds) ...{ timer = new Timer();

JavaScript中timer是如何工作的

作为入门者来说,了解JavaScript中timer的工作方式是很重要的.通常它们的表现行为并不是那么地直观,这是因为它们都处在单线程中.让我们先来看看三个用来出创建和操作timer的函数. var id = setTimeout(fn, delay); 初始化这个timer,然后这个timer将会在delay延时后调用这个函数fn.这个函数将返回一个唯一的ID,可以通过这个ID来取消timer. var id = setInterval(fn, delay); 与setTimeout类似,不过

【JavaScript】JavaScript中的Timer是怎么工作的( setTimeout,setInterval)

原文(http://www.yeeyan.org/articles/view/luosheng/24380) 作为入门者来说,了解JavaScript中timer的工作方式是很重要的.通常它们的表现行为并不是那么地直观,而这是因为它们都处在一个单一线程中.让我们先来看一看三个用来创建以及操作timer的函数. var id = setTimeout(fn, delay); - 初始化一个单一的timer,这个timer将会在一定延时后去调用指定的函数.这个函数(setTimeout)将返回一个唯

javascript中的定时器(How JavaScript Timers Work)

javascript定时器工作原理是一个重要的基础知识点.因为定时器在单线程中工作,它们表现出的行为很直观.我们该如何创建和维护定时器呢?要从如下三个函数(都是定义在全局作用域,在浏览器中就是window的方法)说起: var id=setTimeout(fn,delay);-初始化一个只执行一次的定时器,这个定时器会在指定的时间延迟delay之后调用函数fn,该setTimeout函数返回定时器的唯一id,我们可以通过这个id来取消定时器的执行. var id=setInvertal(fn,d

JavaScript事件驱动机制&定时器机制

在浏览器中,事件作为一个极为重要的机制,给予JavaScript响应用户操作与DOM变化的能力.在NodeJS中.异步事件驱动模型则是提高并发能力的基础. 一.程序怎样响应事件 程序响应外部的事件有两种方式: 1. 中断 操作系统处理键盘等硬件输入就是通过中断来进行的.这个方式的优点是即使没有多线程,我们也能够放心地运行我们的代码,CPU收到中断信号之后自己主动地转去运行对应的中断处理程序,处理完毕后会恢复原来的代码的运行环境继续运行. 这样的方式须要硬件的支持.一般来说都会被操作系统封装起来.

前端 之 JavaScript : JS的面向对象; 定时器; BOM

JS的面向对象 定时器 BOM JS的面向对象 创建对象的几种常用方式 1.使用Object或对象字面量创建对象 2.工厂模式创建对象 3.构造函数模式创建对象 4.原型模式创建对象 1.使用Object或对象字面量创建对象 JS中最基本创建对象的方式: var student = new Object(); student.name = "easy"; student.age = "20"; 这样,一个student对象就创建完毕,拥有2个属性name以及age,

实用篇:说说我在JavaScript项目中使用的工具类

在JavaScript的开发中,我们都会写一些工具类来帮我们简化一些业务操作的逻辑,一下就貼几个我在项目开发过程中常用的工具类.表达能力有限,各位看官还是看源码吧. 一.日期处理工具类. /** * 日期处理工具类 * @Authors: jackyWHJ * @date 2013-10-18 * */ var DateUtils = { /** * 得到日期在一年当中的周数 */ getISOYearWeek: function(date) { var commericalyear = thi

qt中定时器Timer的使用

qt中定时器Timer的使用,布布扣,bubuko.com