JavaScript定时器实现的原理分析


Posted in Javascript onDecember 06, 2016

JavaScript中的定时器大家基本在平时的开发中都遇见过吧,但是又有多少人去深入的理解其中的原理呢?下面我们就来分析一下定时器的实现原理。

一、储备知识

在我们在项目中一般会遇见过这样的两种定时器,第一种是setTimeOut,第二种是setInterval,这两种定时器有如下的区别:

1、setTimeout允许设置一个超时对象,超时后执行这个对象,但是只执行一次,无周期

2、setInternval允许设置一个超时对象,超时后执行这个对象,周期等于超时对象指定的时间,周期为无限循环

举一个简单的例子来说明一下:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>blog案例</title>
</head>
<body>
 <script type="text/javascript">
  setTimeout("alert('this is test')",2000);
  setInterval("console.log('demo');",1000);
 </script>
</body>
</html>

这个运行后的结果是弹出了一次对话框,然后在控制台可以看到每1秒钟会向其中输出demo字样

二、定时器原理初识

那么问题来了,如下的代码运行的时候会出现什么情况呢?

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>blog案例</title>
</head>
<body>
 <script type="text/javascript">
  setTimeout("alert('定时器!')",0);
  alert("测试")
 </script>
</body>
</html>

是先执行alert("测试"),还是先执行alert("定时器")呢,那么我们就来运行一下吧!

运行后的结果是先弹出测试字样的弹出框,然后才弹出定时器字样的弹出框,为什么会这样呢?不是定时器的时间为0就即可执行吗?

答案不是这样的,因为JS众所周知是单线程的,所以很多人会认为在上面的例子中会先阻塞等待定时器执行完成后再执行下面的语句,但是这个也就是单线程的一个缺陷之一吧,为了解决这个问题,引入了异步机制。异步机制主要是利用一个我们平时很少去关注的一个知识点——浏览器的多线程。究竟什么是浏览器的多线程呢?

三、浏览器的多线程

 这里我们就来讲解一下,众所周知,JS是单线程的,但是对于浏览器来说JS的执行只不过是在浏览器众多现成中的一条,我们称之为JS引擎线程。而浏览器的其他线程则是通过JS引擎线程在执行到某个特定的功能之后指定给浏览器的对应线程。具体的原理详见图示:

JavaScript定时器实现的原理分析

 从这张图我们可以知道JS引擎线程首先执行回调函数块,然后是执行点击事件回调,接着是执行定时器的线程,最后在执行其他的线程。

以下面的代码我们来分析一下:

setTimeout("alert('定时器!')",0);
alert("测试")

首先JS线程读取到setTimeout定时器,这个时候就会执行浏览器的线程,然后跳过定时器继续执行,这个时候你就看到了弹出框的内容为测试,然后因为定时器的时间为0,所以一执行定时器线程就会即可将弹出框为定时器字样的任务添加到主线程(JS引擎线程)的队列之后,等待JS引擎的调用,这个时候我们看到的结果是先弹出测试,然后再弹出定时器

另外我们要注意在HTML5规范中规定定时器的定时时间不能小于4ms,如果是小于4ms,则默认为4ms,所以在这个例子中的0,默认的是4ms,但是这个在不通过的浏览器中的表现是不同的,但是这个一般在项目中是没有什么印象的,这个只是仅做了解即可。

 好的我们将上面的代码改写成这样,然后我们再来看看效果:

<script type="text/javascript">
  console.time("test");
  setTimeout("for(var i=0;i<1000;i++)console.log('定时器!');",1000);
  console.log("测试");
  console.timeEnd("test");
 </script>

    运行后的结果如下:

JavaScript定时器实现的原理分析

    这里有几个知识点:

1、console.time和console.timeEnd这两个方法是可以获取在其中间执行的语句所用的时间,从图中我们可以知道test执行的时间在1ms左右,然而定时器的定时时间是在1000ms左右所以这两个语句只能计算当前引擎的执行时间,换句话说就是在浏览器中的定时器模块的运行时间是这样是没法计算的

2、另外我们可以看到一个现象就是定时器在执行的时候不是一千个定时器的字样全都一次性的打印出来,而是几百几百的增加,这个是为什么呢?这里就涉及到了另外的一个问题,如果是定时器的时间到了,但是定时器中的任务没有执行完成这个时候会怎样?

我们上面说过就是定时器的时间到了的情况下,就会向JS引擎线程添加任务,不论任务里面的语句是否执行完成,都会像JS引擎线程队列中添加,但是剩下的未执行完成的语句怎么办呢?

程序执行到了定时器任务的时候,就会先把已经在定时器模块执行过的语句加载一次,然后是继续执行定时器模块的剩余语句。(定时器模块向JS引擎中添加的任务相当于就是C语言中的一个指针,指向的是定时器模块)

所以,setTimeout我们可以定义为:

在指定时间内, 将任务放入事件队列,等待js引擎空闲后被执行.

四、setInterval的使用

setInterval最基础的使用方法是直接当一个循环定时器使用,这里就不举例说明

对于setInterval(fn, 100)容易产生一个误区:并不是上一次fn执行完了之后再过100ms才开始执行下一次fn。 事实上,setInterval并不管上一次fn的执行结果,而是每隔100ms就将fn放入主线程队列,而两次fn之间具体间隔多久就不一定了,跟setTimeout实际延迟时间类似,和JS执行情况有关。具体的延迟效果与内存等因素有关。

五、定时器的可靠性

虽说定时器在大部分的情况下都是趋于稳定的,但是定时器在使用的时候也存在着一些误差

如下所示:

<script type="text/javascript">
  var time1 = new Date().getTime();
  setInterval(function(){
   var time2 = new Date().getTime();
   console.log("setInterval执行的差值时间:"+(time2-time1));
  },1000);
 </script>

运行的结果如下:

JavaScript定时器实现的原理分析

从图中我们基本可以看出定时器存在着一些小小的误差就比如第一次的运行时间为1001ms比我们设定的时间多出了1ms,所以得出结论:定时器不是完全的可靠的,存在极小的误差。这个还是在chrome浏览器上面测试的结果,换是在IE浏览器测试那又如何呢?

JavaScript定时器实现的原理分析

结果显示,在IE浏览器下面的误差更大

六、定时器的妙用

 定时器在项目中除了可以作为定时的作用外还可以用来做耗时代码的优化:

 我们假设有这样的一个场景,就是在某个页面中要渲染50万个节点,这个时候对于一般的项目中,直接渲染是不可取的,因为这个时候会占用过多的内存,导致浏览器出现了卡死的状态,用户误以为是页面卡死而  直接关闭浏览器或者杀死进程,即使是用户不关闭页面这样给用户的体验也是不好的,这个时候我们要怎样来解决这个问题呢,我们可以利用定时器来优化这个问题首先我们可以把50万个节点分成多组,每组渲染  的节点数不要过多,然后通过setInterval来进行循环这个既不阻塞JS引擎线程的运行,又不可以提高渲染的消耗时间。从而达到最终的优化渲染。

七、定时器使用注意事项

 如果是项目中有对个定时器的参与那么记得在一个定时器执行结束的时候记得要调用clearInterval或clearTimeout这两个方法来清除定时器,以免定时器之间互相干扰出现一些抓摸不定的现象

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
jQuery与ExtJS之选择实例分析
Aug 19 Javascript
Jquery判断$(&quot;#id&quot;)获取的对象是否存在的方法
Sep 25 Javascript
js和html5实现手机端刮刮卡抽奖效果完美兼容android/IOS
Nov 18 Javascript
AngularJS入门教程之Scope(作用域)
Jul 27 Javascript
JS中this上下文对象使用方式
Oct 09 Javascript
深入理解JavaScript中的块级作用域、私有变量与模块模式
Oct 31 Javascript
移动端点击态处理的三种实现方式
Jan 12 Javascript
iscroll-probe实现下拉刷新和下拉加载效果
Jun 28 Javascript
React Native预设占位placeholder的使用
Sep 28 Javascript
Vue使用axios出现options请求方法
May 30 Javascript
js tab栏切换代码实例解析
Sep 03 Javascript
JavaScript中10个Reduce常用场景技巧
Jun 21 Javascript
原生js实现弹出层登录拖拽功能
Dec 05 #Javascript
详解Vue.js——60分钟组件快速入门(上篇)
Dec 05 #Javascript
原生js编写基于面向对象的分页组件
Dec 05 #Javascript
8 行 Node.js 代码实现代理服务器
Dec 05 #Javascript
浅谈Node.js:Buffer模块
Dec 05 #Javascript
微信小程序 Windows2008 R2服务器配置TLS1.2方法
Dec 05 #Javascript
详解jquery easyui之datagrid使用参考
Dec 05 #Javascript
You might like
PHP脚本中include文件出错解决方法
2008/11/20 PHP
php 破解防盗链图片函数
2008/12/09 PHP
判定是否原生方法的JS代码
2013/11/12 Javascript
jquery比较简洁的软键盘特效实现方法
2015/03/19 Javascript
JQuery标签页效果的两个实例讲解(4)
2015/09/17 Javascript
JS实现在状态栏显示打字效果完整实例
2015/11/02 Javascript
通过点击jqgrid表格弹出需要的表格数据
2015/12/02 Javascript
JQuery Mobile实现导航栏和页脚
2016/03/09 Javascript
第二篇Bootstrap起步
2016/06/21 Javascript
javascript遍历json对象的key和任意js对象属性实例
2017/03/09 Javascript
详解Vue.js基于$.ajax获取数据并与组件的data绑定
2017/05/26 Javascript
JS按钮闪烁功能的实现代码
2017/07/21 Javascript
微信小程序 动画的简单实例
2017/10/12 Javascript
javascript将json格式数组下载为excel表格的方法
2017/12/22 Javascript
如何手动实现es5中的bind方法详解
2018/12/07 Javascript
基于JS实现前端压缩上传图片的实例代码
2019/05/14 Javascript
使用Layui搭建后台管理界面的操作方法
2019/09/20 Javascript
Python random模块(获取随机数)常用方法和使用例子
2014/05/13 Python
python用来获得图片exif信息的库实例分析
2015/03/16 Python
Python序列对象与String类型内置方法详解
2019/10/22 Python
python将三维数组展开成二维数组的实现
2019/11/30 Python
Python datetime 格式化 明天,昨天实例
2020/03/02 Python
浅谈Django中的QueryDict元素为数组的坑
2020/03/31 Python
Python安装并操作redis实现流程详解
2020/10/13 Python
法国足球商店:Footcenter
2019/07/06 全球购物
以思科路由器为例你写下单臂路由的配置命令
2013/08/03 面试题
交通专业个人自荐信格式
2013/09/23 职场文书
汽车电子与维修专业大学生求职信
2013/09/28 职场文书
学校创先争优活动总结
2014/08/28 职场文书
法定代表人授权委托书范文
2014/09/22 职场文书
委托书的样本
2015/01/28 职场文书
英语邀请函范文
2015/02/02 职场文书
2015年惩防体系建设工作总结
2015/05/22 职场文书
护士岗位竞聘书
2015/09/15 职场文书
解决python绘图使用subplots出现标题重叠的问题
2021/04/30 Python
Vue项目中如何封装axios(统一管理http请求)
2021/05/02 Vue.js