JavaScript的计时器的工作原理

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

原文翻译:

从基础层面来讲,理解JavaScript计时器的工作原理是很重要的。由于JavaScript是单线程的,所以很多时候计时器并不是表现得和我们的直观想象一样。让我们从下面的三个函数开始,它们能够让我们有机会去构造和操作计时器。

  • var id  =setTimeout(fn, delay); -创建了一个简单的计时器,在经过给定的时间后,回调函数将会被执行。这个函数会返回一个唯一的ID,便于在之后某个时间可以注销这个计时器。
  • var id = setInterval(fn, delay); -和setTimeout类似,但是每经过一段时间(给定的延时),所传递的函数就会被执行一次,直到这个定时器被注销。
  • clearInterval(id);clearTimeout(id); -接受一个计时器ID(由之前两种计时器返回)并且停止计时器回调函数的执行。

为了理解计时器的内部工作原理,我们首先需要了解一个非常重要的概念:计时器设定的延时是没有保证的。因为所有在浏览器中执行的JavaScript单线程异步事件(比如鼠标点击事件和计时器)都只有在它有空的时候才执行。这最好通过图片来说明,就如下面这张图所示:

这一张图片里面有很多信息需要慢慢消化,但是彻底地理解这张图片将会让你对JavaScript异步执行是如何工作的有一个更好的认识。这张图片是从一维的角度来阐述的:在垂直方向是以毫秒计的时间,蓝色的块代表了

当前正在执行的JavaScript代码段。比如第一段JavaScript执行了大概18毫秒,鼠标点击事件大概执行了11毫秒。

由于JavaScript每次只能执行一段代码(基于它单线程的特性),所以所有这些代码段都阻塞了其他异步事件的执行。这就意味着,当一件异步事件(比如鼠标点击,计时器触发和一个XMLHttpRequest 请求完成)触发的时候,这些事件的回调函数将排在执行队列的最后去等待执行(排队的方式因浏览器不同而不同,这里只是一个简化)。

一开始,在第一段代码段内,两个计时器被初始化:一个10ms的setTimeout 和一个10ms的setInterval。由于计时器在哪儿初始化就在那儿开始计时,所以实际上计时器在第一段代码执行完成之前就触发了。然而,计时器的回调函数并不是立即执行了(单线程限制了不能这样做),相反的是,回调函数排在了执行队列的最后,等到下一个有空的时间去执行。

此外,在第一个代码块内我们看到了一个鼠标点击事件发生了。与之相关的javascript异步事件(我们不可能预测用户会在什么时候去采取这样的动作,因此这个事件被视为异步的)并不会立即执行。和计时器一样的是,它被放到了队列的最后去等待执行。

在第一个代码快执行完成的时候,浏览器会立即发出这样的询问:谁正在等待执行?这个时候,鼠标点击处理程序和计时器回调函数都在等待执行。浏览器选择了其中一个(鼠标点击回调函数)并且立即执行它。为了执行,计时器会等到下一个可能执行的时间。

我们注意到,当鼠标点击事件对应的处理程序正在执行的时候,第一个定时回调函数也要执行了。同定时计时器一样,它也在队列的后面等待执行。然而,我们可以注意到,当定时器再一次触发(在计时器回调函数正在执行的时候),这一次定时器回调函数被丢弃了。如果在执行一大块代码块的时候,你把所有的定时回调函数都放在队列的最后,结果就是一大串定时回调函数将会没有间隔的一起执行,直到完成。相反,在把更多定时回调函数放到队列之前,浏览器会静静的等待,知道队列中的所有定时回调函数都执行完成。

事实上,我们可以看到,当interval回调函数正在执行的时候,interval第三次被触发。这给我们一个很重要的信息:interval并不关心当前谁在执行,它的回调函数会不加区分地进入队列,即使存在这个回调函数会被丢弃的可能。

最后,当第二个定时回调函数完成执行的时候,我们可以看到javascript引擎已经没有什么需要执行了。这意味着,浏览器现在正在等待一个新的异步事件的发生。我们可以看到在50ms的时候,定时回调函数再一次被触发。然而,这一次,没有其他代码阻塞他的执行了,所以他立即执行了定时回调函数。

让我们看一个例子来更好地阐述setTimeout 和setInterval的区别。

1 setTimeout(function(){
2     /* Some long block of code... */
3     setTimeout(arguments.callee, 10);
4   }, 10);
5
6   setInterval(function(){
7     /* Some long block of code... */
8   }, 10);

第一眼看上去这两段代码在功能上是等价的,但事实上却不是。值得注意的是,setTimeout 这段代码会在每次回调函数执行之后至少需要延时10ms再去执行一次(可能是更多,但是不会少)。但是setInterval会每隔10ms就去尝试执行一次回调函数,不管上一个回调函数是不是还在执行。

从这里我们能够学到很多,让我们来概括一下:

  • javascript引擎只有一个线程,迫使异步事件只能加入队列去等待执行。
  • 在执行异步代码的时候,setTimeout 和setInterval 是有着本质区别的。
  • 如果计时器被正在执行的代码阻塞了,它将会进入队列的尾部去等待执行直到下一次可能执行的时间出现(可能超过设定的延时时间)。
  • 如果interval回调函数执行需要花很长时间的话(比指定的延时长),interval有可能没有延迟背靠背地执行。

上述这一切对于理解js引擎是如果工作的无疑是很重要的知识,尤其是大量的典型的异步事件发生时,对于构建一个高效的应用代码片段来说是一个非常有利的基础。

个人见解:

翻译完成之后,感觉对于javascript异步有了新的认识,但是可能初学者看不太懂这篇文章,于是写了一个demo,运行在nodejs环境下(浏览器不容易模拟)

 1 var startTime = new Date();
 2
 3 //初始化计时器
 4 var start = setTimeout(function() {
 5     var end = new Date();
 6     console.log(‘10ms的计时器执行完成,距离程序开始‘ + (end - start) + ‘ms‘);
 7 }, 10);
 8
 9 //模拟鼠标点击事件
10 function asyncReal(data, callback) {
11     process.nextTick(function() {
12         callback();
13      });
14 }
15 var asyncStart = new Date();
16 asyncReal(‘yuanzm‘, function() {
17     var asyncEnd = new Date();
18     console.log(‘模拟鼠标执行事件完成,花费时间‘ + (asyncEnd - asyncStart) + ‘ms‘);
19 })
20
21 //设定定时器
22 count = 1;
23 var interval = setInterval(function() {
24     ++count;
25     if(count === 5) {
26         clearInterval(interval);
27     }
28     console.log(‘定时器事件‘);
29 },10);
30
31 //模拟第一阶段代码执行
32 var first = [];
33 var start = new Date();
34 for(var i = 0;i < 10000000;i++){
35     first.push(i);
36 }
37 var end = new Date();
38 console.log(‘第一阶段代码执行完成,用时‘ + (end - start) + ‘ms‘);

运行结果如下:

我们按照文中的原理来解释一下:

(1) 一开始设定的计时器并不是在10ms后立即执行,而是被添加到了队列后面,等到第一阶段代码执行完成才执行,距离开始的时间也不是设定的10ms

(2)鼠标点击事件同样因为是异步事件,添加到了队列后面,等到第一阶段代码执行完成的时候才执行。

(3)鼠标点击事件先于计时器事件添加到队列后面

(4)最后定时器才能执行

时间: 2024-07-31 14:26:40

JavaScript的计时器的工作原理的相关文章

JavaScript中this的工作原理以及注意事项

在JavaScript中,this 的概念比较复杂.除了在面向对象编程中,this 还是随处可用的.这篇文章介绍了this 的工作原理,它会造成什么样的问题以及this 的相关例子. 要根据this 所在的位置来理解它,情况大概可以分为3种: 1.在函数中:this 通常是一个隐含的参数. 2.在函数外(顶级作用域中):在浏览器中this 指的是全局对象:在Node.js中指的是模块(module)的导出(exports). 3.传递到eval()中的字符串:如果eval()是被直接调用的,th

javascript中闭包的工作原理

一.什么是闭包? 官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包.不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”.看下面这段代码: 1 2 3 4 5 6 7 function a() { var i = 0; function b() { ale

JavaScript prototype背后的工作原理

首先从一个函数说起 function fn1(name, age) { this.name = name; this.age = age; this.say = function() { alert('my name:' + this.name + 'and age:'+ this.age); } } fn1.prototype.test = function() { alert('test'); } var f1 = new fn1('jm',20); f1.say(); //弹出 my na

How Javascript works (Javascript工作原理) (十四) 解析,语法抽象树及最小化解析时间的 5 条小技巧

个人总结:读完这篇文章需要15分钟,文章介绍了抽象语法树与js引擎解析这些语法树的过程,提到了懒解析--即转换为AST的过程中不直接进入函数体解析,当这个函数体需要执行的时候才进行相应转换.(因为有的函数体只是声明了,并没有实际被调用) 解析,语法抽象树及最小化解析时间的 5 条小技巧 这是 JavaScript 工作原理的第十四章. 概述 我们都知道运行一大段 JavaScript 代码性能会变得很糟糕.代码不仅仅需要在网络中传输而且还需要解析,编译为字节码,最后运行.之前的文章讨论了诸如 J

我们应该如何去了解JavaScript引擎的工作原理

我们应该如何去了解JavaScript引擎的工作原理 JavaScript探秘:编写可维护的代码的重要性 JavaScript探秘:谨慎使用全局变量 JavaScript探秘:var预解析与副作用 JavaScript探秘:for循环(for Loops) JavaScript探秘:for-in循环(for-in Loops) JavaScript探秘:Prototypes强大过头了 JavaScript探秘:eval()是"魔鬼" JavaScript探秘:用parseInt()进行

javascript引擎工作原理

1. 什么是JavaScript解析引擎? 简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序.比方说,当你写了 var a = 1 + 1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2. 学过编译原理的人都知道,对于静态语言来说(如Java.C++.C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpreter)

JavaScript工作原理

HTML代码所表示的文档是一种静态文档,几乎没有交互功能,很难使页面成为动态页面.增加脚本语言,可使数据发送到服务器之前先进行处理和校验,动态地创建新的Web内容,更重要的是,引入脚本语言使我们有了事件驱动的软件开发环境. (1)JavaScript简介 JavaScript的原名叫LiveScript,是NetScape公司在引入Sun公司有关Java的程序设计概念后,重新设计而更名的.JavaScript是一种可以嵌入HTML文档的,基于对象并具有某些面向对象特征的脚本语言. 说明:浏览器端

JavaScript动画工作原理之(完结篇)

原作者:Steven Riche 发布时间:2014年2月18日 原文链接:http://code.tutsplus.com/tutorials/javascript-animation-that-works-part-4-of-4--net-35263 翻译:子毅 --------- 将JavaScript进行到底 碎碎两句 折腾了一个多月,杂七杂八的事情加在一起,简直糟透了.博客停了大概有一个月了,从今天起一切都是新的,做好自己就OK了 ---------------------------

JavaScript引擎的工作原理

(转) 1. 什么是JavaScript解析引擎? 简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序.比方说,当你写了 var a = 1 + 1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2. 学过编译原理的人都知道,对于静态语言来说(如Java.C++.C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpre