因为自己在平时工作中,有些功能需要用到定时器,但是定时器并不像我们表边上看到的那样,所以这周末我看看书查查资料,深入研究了一下JavaScript中的定时器,那么废话不多说,下面进入我们今天的正题。

大家都知道JavaScript是单线程的,所以不管是定时器还是用户的操作都是需要在线程队列中排队执行的。

一、定时器在执行线程队列里的分析

为了更好的理解我还是直接写个测试代码来看一下(由于有人反映例子不是很直观不好理解,所以我做了一个新的例子,修改于2016年8月29日),这样分析起来更直观一些

 <meta charset="utf-8">
</head>
<body>
<button id="btn1">开始</button>
<script>
var oBtn1= document.getElementById("btn1");
function createEle(num){
for (i=0;i<num;i++){
var oDiv = document.createElement("div");
oDiv.innerHTML = i+1;
}
}
oBtn1.addEventListener("click",function(){
var time1 = new Date().getTime();
createEle(20000);
var time2 = new Date().getTime();
console.log("普通函数1执行完的时间:"+(time2-time1));
setTimeout(function(){
var time3 = new Date().getTime();
console.log("第一个setTimeout里面开始执行的时间:"+(time3-time1));
createEle(20000);
var time4 = new Date().getTime();
console.log("setTimeout里面的函数运行时间:"+(time4-time3));
},50);
setInterval(function(){
var time5 = new Date().getTime();
console.log("setInterval开始执行的时间:"+(time5-time1));
createEle(20000);
},50);
setTimeout(function(){
var time6 = new Date().getTime();
console.log("第二个setTimeout里面开始执行的时间:"+(time6-time1));
createEle(20000);
},100);
createEle(20000);
var time7 = new Date().getTime();
console.log("普通函数2执行完的时间:"+(time7-time1));
},false); </script>
</body>
</html>

在上面这段测试代码里面一共分为5个部分:

1.创建dom节点

2.第一个setTimeout函数延迟50ms

3.setInterval函数每隔50ms

4.第二个setTimeout函数延迟100ms

5.创建dom节点

没有基础的人一般认为都是按顺序执行呗,这有啥好说的,有点基础的人会认为先执行两个创建dom节点的函数,再延迟50ms执行第一个setTimeout里面的内容,并且每隔50ms会触发setInterval,然后延迟100ms执行第二个setTimeout里面的内容。但是事实并是这样的!!!

下面我贴出代码的运行结果来逐一的分析

从运行结果可以看出,首先结果打印普通函数1运行需要花费170ms(多次刷新页面可能这个时间会不一样,所以我们先姑且认为这个普通函数执行需要170ms)。下面接着打印了普通函数2运行需要花费170ms,然后下面才是定时器的执行时间打印结果。到这里就可以看出我要证明的第一个结论:1.js中的定时器是在当前函数里,所有普通函数执行完成才开始执行定时器的。

下面我再具体分析一下定时器在这期间的运行过程,来揭示下面的结论。

在程序里执行到170ms的时候,第一个普通函数执行完,这时由于还有其他普通函数的存在,所以setTimeout和setInterval都进入等待队列并且开始计时,在220ms的时候由于还有普通函数2在执行占用着线程,所以setTimeout里面的函数无法执行,并且setInterval被二次触发,但是也因为有其他普通函数占用着线程,所以依旧在队列中等待执行。

从第二行执行结果可以看出在350ms的时候第二个普通函数已经执行完成,至此所有定时器以外的函数全部执行完毕,从第三行执行结果可以看出在351ms的时候第一个setTimeout立刻被执行,而并没有再等待50ms,这是因为上面已经提到了第一个setTimeout在第一个普通函数执行完的170ms的时候就开始计时了,只是因为50ms之后,第二个普通函数依然占用着线程导致setTimeout无法执行,所以在所有定时器以外的函数全部执行完毕之后,第一个定时器立刻开始执行里面的内容。这也就是我要证明的第二个结论:2.定时器即使指定了时间,也不一定就能在指定的时间执行(只能比设置的时间长,不会比设置的时间短)。

有人可能会想那么351ms的时候setInterval也应该被触发之后每50ms触发一次,但是在351ms的时候setInterval并没有被执行,这是因为第一个setTimeout里面的函数还在执行,所以setInterval还是在等待队列中等待,在514ms的时候第一个setTimeout里面的函数执行完毕,则开始触发setInterval。这是因为设置的时间都是相同的(50ms),所以就是按定时器函数写的顺序来执行的。

如果我们把setInterval改成30ms的话,那么在第一个setTimeout之前是会先执行一次setInterval的。代码如下:

 <html>
<head>
<title>JavaScript定时器test</title>
<meta charset="utf-8">
</head>
<body>
<button id="btn1">开始</button>
<script>
var oBtn1= document.getElementById("btn1");
function createEle(num){
for (i=0;i<num;i++){
var oDiv = document.createElement("div");
oDiv.innerHTML = i+1;
}
}
oBtn1.addEventListener("click",function(){
var time1 = new Date().getTime();
createEle(20000);
var time2 = new Date().getTime();
console.log("普通函数1执行完的时间:"+(time2-time1));
setTimeout(function(){
var time3 = new Date().getTime();
console.log("第一个setTimeout里面开始执行的时间:"+(time3-time1));
createEle(20000);
var time4 = new Date().getTime();
console.log("setTimeout里面的函数运行时间:"+(time4-time3));
},50);
setInterval(function(){
var time5 = new Date().getTime();
console.log("setInterval开始执行的时间:"+(time5-time1));
createEle(20000);
},30);
setTimeout(function(){
var time6 = new Date().getTime();
console.log("第二个setTimeout里面开始执行的时间:"+(time6-time1));
createEle(20000);
},100);
createEle(20000);
var time7 = new Date().getTime();
console.log("普通函数2执行完的时间:"+(time7-time1));
},false); </script>
</body>
</html>

修改之后的执行结果如下图所示:

从图上结果也是可以引出我要说的第三个结论:3.定时器如果设置的时间都相同,是按照定时器函数写的顺序来执行的,如果设置的时间不同,则是时间设置小的定时器函数会先执行,时间设置大的后执行。

接着之前的分析,在setInterval被触发之后,由于setInterval里面的函数没有执行完(也是需要170ms),所以第二个setTimeout函数没法立即被执行,需要等待setInterval里面的函数执行完毕之后,这也是再一次印证了我上面的结论2,后面就是触发第二个setTimeout函数,等第二个setTimeout函数里面的函数执行完下面依次执行setInterval。

小结:

1.js中的定时器是在当前函数里,所有普通函数执行完成才开始执行定时器的。

2.定时器即使指定了时间,也不一定就能在指定的时间执行(只能比设置的时间长,不会比设置的时间短)。

3.定时器如果设置的时间都相同,是按照定时器函数写的顺序来执行的,如果设置的时间不同,则是时间设置小的定时器函数会先执行,时间设置大的后执行。

二、setTimeout和setInterval之间的区别

还是直接上代码来进行对比

     setTimeout(function cb(){
console.log("setTimeout");
setTimeout(cb,10);
},10);
setInterval(function(){
console.log("setInterval");
},10);

上面的代码看上去功能似乎是一样的,实际上两者是有区别的,在setTimeout里要等里面的函数执行完(也就是前一个callback)再延迟10ms(甚至更久)才可以再次执行回调函数,而setInterval是每隔10ms就会去执行函数里面的内容,而不去管上一个是否执行完。这就是两者之间的区别。

三、定时器延迟时间的可靠性

看了我第一部分贴出的运行结果之后,细心的人可能会发现我打印的定时器延迟会有1-2ms的偏差,就是下面我要看的延迟时间可靠性的问题了

废话不多说直接上测试代码(为了测试效果这里我极端一点把延迟时间设置为1ms,这样数据结果会更加明显一点)

     var time1 = new Date().getTime();
setInterval(function(){
var time2 = new Date().getTime();
console.log("setInterval执行的差值时间:"+(time2-time1));
},1);

常理上来讲运行的结果应该是1 2 3 4 5 6 8 9 ...

但是实际的运行结果如下:

可以看到平均的延迟时间大概在5ms左右,我是拿chrome浏览器测试的,据说不同的系统下不同的浏览器这个平均的时间都不一样,如果爱钻牛角尖的小伙伴可以去尝试一下,这里就不深入展开了.

所以说定时器的延迟时间不宜设置过小,因为太小的话可能根本也达不到你想要的效果(不排除一些真可以控制在1ms左右的牛逼浏览器),而且根据设备硬件或者浏览器的不同可能延迟时间也会有少量的误差

四、定时器的小妙用

有人会说定时器能有什么妙用,无外乎就是在手机翻转屏的时候延迟几秒获取设备宽度等等。

我想说的不是这些而是利用定时器来提高性能的办法。

首先我们来模拟一个js要动态创建十万个DOM节点的场景,这种情况浏览器会花费大量的时间来执行,从而阻塞了其他代码的执行。这时如果我们使用定时器把这十万个DOM打散分成多个部分,这样一下就好了很多。

五、合理管理定时器

大家都知道定时器,用完之后需要清除,如果不清楚同时多个定时器在一个页面上跑,会损耗性能让页面浏览起来有卡顿感,尤其是定时器在动画里面的应用,想象一下,如果制作一个动画效果里面用了许多定时器,并且多个定时器同时运行,那么有可能就会出现本来后面要执行的一个动画效果在前面就被提前执行了,这是我们不想看到的。

所以我们就要根据情况对定时器做一个合理的管理,还是拿做动画为例,

1.动画肯定要保持同一时间只执行一个定时器;

2.并且自己可以灵活的控制定时器的开启和关闭。

在网上可以找到管理定时器的示例代码,大家可以参考一下:

     var timers = {
timerID : 0,
timers : [],
add : function (fn) {
this.timers.push(fn);
},
start : function (){
if(this.timerID) return;
(function runNext(){
if(timers.timers.length>0){
for(var i=0;i<timers.timers.length;i++){
if(timers.timers[i]() === false){
timers.timers.splice(i,1);
i--;
}
}
timers.timerID = setTimeout(runNext,10);
}
})();
},
stop : function(){
clearTimeout(this.timerID);
this.timerID = 0;
}
};

其实管理的核心还是我上面提到的两条。

总结

1.js中的定时器是在当前函数里,所有普通函数执行完成才开始执行定时器的;

2.定时器即使指定了时间,也不一定就能在指定的时间执行(只能比设置的时间长,不会比设置的时间短);

3.定时器如果设置的时间都相同,是按照定时器函数写的顺序来执行的,如果设置的时间不同,则是时间设置小的定时器函数会先执行,时间设置大的后执行;

4. setTimeout和setInterval在被触发的定义上是有很大区别的;

5.定时器的延迟时间不宜设置过小;

6.利用定时器可以分解大量操作的代码;

7.合理管理页面中的定时器。

感谢大家的阅读,有什么分析的不对的地方欢迎大家批评指出,如果喜欢本文,请点击右下角的推荐哦~

关于JavaScript定时器我的一些小理解的更多相关文章

  1. 【JavaScript定时器小案例】常见的几种定时器实现的案例

    [JavaScript定时器小案例]常见的几种定时器实现的案例 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 在日常开发 ...

  2. Javascript定时器(三)——setTimeout(func, 0)

    setTimeout(func, 0)可以使用在很多地方,拆分循环.模拟事件捕获.页面渲染等 一.setTimeout中的delay参数为0,并不是指马上执行 <script type=&quo ...

  3. Javascript定时器(二)——setTimeout与setInterval

    一.解释说明 1.概述 setTimeout:在指定的延迟时间之后调用一个函数或者执行一个代码片段 setInterval:周期性地调用一个函数(function)或者执行一段代码. 2.语法 set ...

  4. js对象详解(JavaScript对象深度剖析,深度理解js对象)

    js对象详解(JavaScript对象深度剖析,深度理解js对象) 这算是酝酿很久的一篇文章了. JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕 ...

  5. javascript引擎执行的过程的理解--执行阶段

    一.概述 同步更新sau交流学习社区(nodeJSBlog):javascript引擎执行的过程的理解--执行阶段 js引擎执行过程主要分为三个阶段,分别是语法分析,预编译和执行阶段,上篇文章我们介绍 ...

  6. Javascript 定时器调用传递参数的方法

    文章来源:  https://m.jb51.net/article/20880.htm 备注:先记下,以后整理: Javascript 定时器调用传递参数的方法,需要的朋友可以参考下. 无论是wind ...

  7. JavaScript定时器的工作原理(翻译)

    JavaScript定时器的工作原理(翻译) 标签(空格分隔): JavaScript定时器 最近在看ajax原理的时候,看到了一篇国外的文章,讲解了JavaScript定时器的工作原理,帮助我很好的 ...

  8. javascript编写一个简单的编译器(理解抽象语法树AST)

    javascript编写一个简单的编译器(理解抽象语法树AST) 编译器 是一种接收一段代码,然后把它转成一些其他一种机制.我们现在来做一个在一张纸上画出一条线,那么我们画出一条线需要定义的条件如下: ...

  9. JavaScript定时器详解

    假设有以下场景 setTimeout(function timeoutHandler(){ /*Some timeout handle code that runs for 6ms*/ }, 10); ...

随机推荐

  1. LL(1)文法--递归下降程序

    递归下降程序 递归下降程序一般是针对某一个文法的.而递归下降的预测分析是为每一个非终结符号写一个分析过程,由于文法本身是递归的,所以这些过程也是递归的. 以上是前提. Sample 假如给的是正规式子 ...

  2. [译文]程序员能力矩阵 Programmer Competency Matrix

    注意:每个层次的知识都是渐增的,位于层次n,也蕴涵了你需了解所有低于层次n的知识. 计算机科学 Computer Science   2n (Level 0) n2 (Level 1) n (Leve ...

  3. 开发Web版一对一远程直播教室只需30分钟 - 使用face2face网络教室

    转载自:https://blog.csdn.net/wo_shi_ma_nong/article/details/88110111 在“为网站开发远程直播教室的折腾过程及最终实现”中,介绍了如何使用f ...

  4. UVA 400 - Unix ls (Unixls命令)

    csdn : https://blog.csdn.net/su_cicada/article/details/86773007 例题5-8 Unixls命令(Unix ls,UVa400) 输入正整数 ...

  5. 扫描算法(SCAN)——磁盘调度管理

    原创 上一篇博客写了最短寻道优先算法(SSTF)——磁盘调度管理:http://www.cnblogs.com/chiweiming/p/9073312.html 此篇介绍扫描算法(SCAN)——磁盘 ...

  6. R语言学习笔记—朴素贝叶斯分类

    朴素贝叶斯分类(naive bayesian,nb)源于贝叶斯理论,其基本思想:假设样本属性之间相互独立,对于给定的待分类项,求解在此项出现的情况下其他各个类别出现的概率,哪个最大,就认为待分类项属于 ...

  7. Oracle入门第四天(下)——约束

    一.概述 1.分类 表级约束主要分为以下几种: NOT NULL UNIQUE PRIMARY KEY FOREIGN KEY CHECK 2.注意事项 如果不指定约束名 ,Oracle server ...

  8. linux 下c语言的pwd 实现

    20155211实现mypwd 关于pwd 在Linux层次结构中,用户可以在被授权的任意目录下利用mkdir命令创建新目录,也可以利用cd命令从一个目录转换到另一个目录.然而,没有提示符来告知用户目 ...

  9. 基于fork(),execvp()和wait()实现类linux下的bash——mybash

    基于fork(),execvp()和wait()实现类linux下的bash--mybash 预备知识 fork():fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可 ...

  10. 20155234 实验二 Java面向对象程序设计

    实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验步骤 (一)单元测试 ...