JavaScrip单线程引擎工作原理分析_javascript技巧

从基础的层面来讲,理解JavaScript的定时器是如何工作的是非常重要的。定时器的执行常常和我们的直观想象不同,那是因为JavaScript引擎是单线程的。我们先来认识一下下面三个函数是如何控制计时器的。

推荐阅读:雕虫无小技 JavaScript初学者的10个迷你技巧

复制代码 代码如下:

var id = setTimeout(fn, delay);

初始化一个计时器,然后在指定的时间间隔后执行。该函数返回一个唯一的标志ID(Number类型),我们可以使用它来取消计时器。

复制代码 代码如下:

var id = setInterval(fn, delay);

和setTimeout有些类似,但它是连续调用一个函数(时间间隔是delay参数)直到它被取消。

复制代码 代码如下:

clearInterval(id);, clearTimeout(id);

使用计时器ID(setTimeout 和 setInterval的返回值)来取消计时器回调的发生。

为了理解计时器的内在执行原理,有一个重要的概念需要加以探讨:计时器的延迟(delay)是无法得到保障的。由于所有JavaScript代码是在一个线程里执行的,所有异步事件(例如,鼠标点击和计时器)只有拥有执行机会时才会执行。用一个很好的图表加以说明:

在这个图表中有许多信息需要理解,如果完全理解了它们,你会对JavaScript引擎如何实现异步事件有一个很好的认识。这是一个一维的图标:垂直方向表示时间,蓝色的区块表示JavaScript代码执行块。例如第一个JavaScript代码执行块需要大约18ms,鼠标点击所触发的代码执行块需要11ms等等。

由于JavaScript引擎同一时间只执行一条代码(这是由于JavaScript单线程的性质),所以每一个JavaScript代码执行块会“阻塞”其它异步事件的执行。这就意味着当一个异步事件发生(例如,鼠标点击,计时器被触发,或者Ajax异步请求)后,这些事件的回调函数将排在执行队列的最后等待执行(实际上,排队的方式根据浏览器的不同而不同,所以这里只是一个简化);

从第一个JavaScript执行块开始研究,在第一个执行块中两个计时器被初始化:一个10ms的setTimeout()和一个10ms的setInterval()。依据何时何地计时器被初始化(计时器初始化完毕后就会开始计时),计时器实际上会在第一个代码块执行完毕前被触发。但是,计时器上绑定的函数不会立即执行(不被立即执行的原因是JavaScript是单线程的)。实际上,被延迟的函数将依次排在执行队列的最后,等待下一次恰当的时间再执行。

此外,在第一个JavaScript执行块中我们看到了一个“鼠标点击”事件发生了。一个JavaScript回调函数绑定在这个异步事件上了(我们从来不知道用户什么时候执行这个(点击)事件,因此认为它是异步的),这个函数不会被立即执行,和上面的计时器一样,它将排在执行队列的最后,等待下一次恰当的时候执行。

当第一个JavaScript执行块执行完毕后,浏览器会立即问一个问题:哪个函数(语句)在等待被执行?在这时,一个“鼠标点击事件处理函数”和一个“计时器回调函数”都在等待执行。浏览器会选择一个(实际上选择了“鼠标点击事件的处理函数”,因为由图可知它是先进队的)立即执行。而“计时器回调函数”将等待下次适合的时间执行。

注意,当“鼠标点击事件处理函数”执行的时候,setInterval的回调函数第一次被触发了。和setTimeout的回调函数一样,它将排到执行队列的最后等待执行。但是,一定要注意这一点:当setInterval回调函数第二次被触发时(此时setTimeout函数仍在执行)setTimeout的第一次触发将被抛弃掉。当一个很长的代码块在执行时,可能把所有的setInterval回调函数都排在执行队列的后面,代码块执行完之后,结果便会是一大串的setInterval回调函数等待执行,并且这些函数之间没有间隔,直到全部完成。所以,浏览器倾向于的当没有更多interval的处理函数在排队时再将下一个处理函数排到队尾(这是由于间隔的问题)。

我们能够发现,当第三个setInterval回调函数被触发时,之前的setInterval回调函数仍在执行。这就说明了一个很重要的事实:setInterval不会考虑当前正在执行什么,而把所有的堵塞的函数排到队列尾部。这意味着两次setInterval回调函数之间的时间间隔会被牺牲掉(缩减)。

最后,当第二个setInterval回调函数执行完毕后,我们可以看到没有任何程序等待JavaScript引擎执行了。这就意味着浏览器现在在等待一个新的异步事件的发生。在50ms时一个新的setInterval回调函数再次被触发,这时,没有任何的执行块阻塞它的执行了。所以它会立刻被执行。让我们用一个例子来阐明setTimeout和setInterval之间的区别:

复制代码 代码如下:

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

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

这两句代码乍一看没什么差别,但是它们是不同的。setTimeout回调函数的执行和上一次执行之间的间隔至少有10ms(可能会更多,但不会少于10ms),而setInterval的回调函数将尝试每隔10ms执行一次,不论上次是否执行完毕。

总结

◆JavaScript引擎是单线程的,强制所有的异步事件排队等待执行;

◆setTimeout和 setInterval在执行异步代码的时候有着根本的不同;

◆如果一个计时器被阻塞而不能立即执行,它将延迟执行直到下一次可能执行的时间点才被执行(比期望的时间间隔要长些);

◆如果setInterval回调函数的执行时间将足够长(比指定的时间间隔长),它们将连续执行并且彼此之间没有时间间隔。

上述这些知识点都是非常重要的。了解了JavaScript引擎是如何工作的,尤其是大量的异步事件(连续)发生时,才能为构建高级应用程序打好基础。

原文作者:John Resig

原文链接: http://ejohn.org/blog/how-javascript-timers-work/

时间: 2025-01-07 16:38:40

JavaScrip单线程引擎工作原理分析_javascript技巧的相关文章

javascript 定时器工作原理分析_javascript技巧

setTimeout() MDN对 setTimeout 的定义为: 在指定的延迟时间之后调用一个函数或执行一个代码片段. 语法 setTimeout 的语法非常简单,第一个参数为回调函数,第二个参数为延时的时间.函数返回一个数值类型的ID唯一标示符,此ID可以用作 clearTimeout 的参数来取消定时器: var timeoutID = window.setTimeout(code, delay); IE0+ 还支持回调参数的传入: var timeoutID = window.setT

JavaScript定时器实现的原理分析_javascript技巧

JavaScript中的定时器大家基本在平时的开发中都遇见过吧,但是又有多少人去深入的理解其中的原理呢?下面我们就来分析一下定时器的实现原理. 一.储备知识 在我们在项目中一般会遇见过这样的两种定时器,第一种是setTimeOut,第二种是setInterval,这两种定时器有如下的区别: 1.setTimeout允许设置一个超时对象,超时后执行这个对象,但是只执行一次,无周期 2.setInternval允许设置一个超时对象,超时后执行这个对象,周期等于超时对象指定的时间,周期为无限循环 举一

js实现跨域的4种实用方法原理分析_javascript技巧

什么是js跨域呐? js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作是不同的域. 要解决跨域的问题,我们可以使用以下几种方法: 一.通过jsonp跨域 在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的.但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的. 比如,有个a.html页面,

理解Javascript_01_理解内存分配原理分析_javascript技巧

原始值和引用值 在ECMAScript中,变量可以存放两种类型的值,即原始值和引用值. 原始值指的就是代表原始数据类型(基本数据类型)的值,即Undefined,Null,Number,String,Boolean类型所表示的值. 引用值指的就是复合数据类型的值,即Object,Function,Array,以及自定义对象,等等 栈和堆 与原始值与引用值对应存在两种结构的内存即栈和堆 栈是一种后进先出的数据结构,在javascript中可以通过Array来模拟栈的行为 复制代码 代码如下: va

js下获取div中的数据的原理分析_javascript技巧

关于从中学到的知识: document.getelementbyid("ddhdh").innerHTML 可以获取到div中的全部数据,包括标签...但是只是在IE和OPERA中使用 document.getelementbyid("ddhdh").innerTEXT 可以获取到div中的文本数据,不会获取到标签...但是只是在IE和OPERA中使用 document.getElementById("text").textContent 用于在

JavaScript中的闭包原理分析_javascript技巧

我们来看一个定义: Closure 所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 这说明了,JavaScript中的闭包是包含了上下文的函数,也就是说,这个函数的作用基础,是它所处的环境,这是不能超越的,跟线性代数是不是有一点似曾相识的感觉呢? 换个角度看,闭包的作用是为了实现OO.JavaScript中,没有像C++那样的public.private.protect属性标识, 建立起类比较困难."类

色彩搭配原理分析及技巧

色彩搭配原理分析及技巧: 研究色彩是为了使用色彩,也就是说最大限度地发挥色彩的作用.色彩的意义与内容在艺术创造和表现方面是复杂多变的,但在欣赏和解释方面又有共通的国际特性,可见它在人们心目中不但是活的,也是一种很美的大众语言.所以,通过对色彩的各种心理分析,找出它们的各种特性,可以做到合理而有效地使用色彩. 一.红色系 红色系是从色相环上的红紫色到朱红色之间的色彩.它的刺激作用很大,具有很高的注目性和视认性.大红色是暖色系里温度最高的色彩,红色系原色彩对人的心理能产生很大的鼓舞作用. 1.纯色使

Java规则引擎工作原理及其应用

摘 要 Java规则引擎是一种嵌入在Java程序中的组件,它的任务是把当前提交给引擎的Java数据对象与加载在引擎中的业务规则进行测试和比对,激活那些符合当前数据状态下的业务规则,根据业务规则中声明的执行逻辑,触发应用程序中对应的操作. 引言 目前,Java社区推动并发展了一种引人注目的新技术--Java规则引擎(Rule Engine).利用它就可以在应用系统中分离商业决策者的商业决策逻辑和应用开发者的技术决策,并把这些商业决策放在中心数据库或其他统一的地方,让它们能在运行时可以动态地管理和修

MultiDex工作原理分析和优化方案

动态加载技术(插件化)系列已经坑了有一段时间了,不过UP主我并没有放弃治疗哈,相信在不就的未来就可以看到"系统Api Hook模式"和插件化框架Frontia的更新了.今天要讲的是动态加载技术的亲戚 -- MultiDex.他们的核心原理之一都是dex文件的加载. MultiDex是Google为了解决"65535方法数超标"以及"INSTALL_FAILED_DEXOPT"问题而开发的一个Support库,具体如何使用MultiDex现在市面已