关于JavaScript定时器我的一些小理解

时间:2022-09-24 22:34:34

因为自己在平时工作中,有些功能需要用到定时器,但是定时器并不像我们表边上看到的那样,所以这周末我看看书查查资料,深入研究了一下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里面的内容。但是事实并是这样的!!!

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

关于JavaScript定时器我的一些小理解

从运行结果可以看出,首先结果打印普通函数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>

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

关于JavaScript定时器我的一些小理解

从图上结果也是可以引出我要说的第三个结论: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 ...

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

关于JavaScript定时器我的一些小理解

可以看到平均的延迟时间大概在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定时器&lpar;三&rpar;——setTimeout&lpar;func&comma; 0&rpar;

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

  3. Javascript定时器&lpar;二&rpar;——setTimeout与setInterval

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

  4. js对象详解&lpar;JavaScript对象深度剖析,深度理解js对象&rpar;

    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编写一个简单的编译器&lpar;理解抽象语法树AST&rpar;

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

  9. JavaScript定时器详解

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

随机推荐

  1. hdu 4358 Boring counting dfs序&plus;莫队&plus;离散化

    Boring counting Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 98304/98304 K (Java/Others) ...

  2. YUM软件管理

    YUM是一个RPM的前端程序,主要目的是设计用来解决RPM的依赖关系的问题,而不用手动安装所依赖的所有软件.它使用仓库保存管理RPM软件包,仓库的配置文件保存在/etc/yum.repos.d/目录下 ...

  3. 点击Button后,执行MouseDown的过程(使用Call Stack观察很清楚)

    Form1上放两个按钮Button1和Button2,默认输入焦点是Button1,现在点击Button2,产生WM_LBUTTONDOWN消息 procedure TForm1.Button2Mou ...

  4. Redhat 显示系统版本号和内核版本号

    1./etc/issue 和 /etc/redhat-release都是系统安装时默认的发行版本信息,通常安装好系统后文件内容不会发生变化.[root@rac1 mysql]# cat /etc/is ...

  5. &lbrack;刷题&rsqb;算法竞赛入门经典&lpar;第2版&rpar; 5-12&sol;UVa511 - Do You Know the Way to San Jose&quest;

    题意:N张地图,查找某地点在不在某些地图上,若在,使用细节多的地图.使用哪个地图的破要求挺多,细心一点就好. 代码:(Accepted,0.000s) //UVa511 - Do You Know t ...

  6. svo笔记

    使用 要想在ros中有更多的debug信息,要在global.h中把ros log的级别设为debug,最简单的就是把SVO_DEBUG_STREAM(x)改成ROS_INFO_STREAM(x) # ...

  7. Spring之旅第四篇-注解配置详解

    一.引言 最近因为找工作,导致很长时间没有更新,找工作的时候你会明白浪费的时间后面都是要还的,现在的每一点努力,将来也会给你回报的,但行好事,莫问前程!努力总不会有错的. 上一篇Spring的配置博客 ...

  8. Leetcode&colon; Max Consecutive Ones II&lpar;unsolved locked problem&rpar;

    Given a binary array, find the maximum number of consecutive 1s in this array if you can flip at mos ...

  9. mysql 使用正则表达式查询

    SELECT * FROM `qq` where qq_name!='no' and qq_gender='女' and qq_location!='no' and qq_location!='' a ...

  10. jQuery封装 写的的确不错 转载

    扩展jQuery插件和方法的作用是非常强大的,它可以节省大量开发时间.这篇文章将概述jQuery插件开发的基本知识,最佳做法和常见的陷阱. 入门 编写一个jQuery插件开始于给jQuery.fn加入 ...