javascript 定时器工作原理分析


Posted in Javascript onDecember 03, 2016

setTimeout()

MDN对 setTimeout 的定义为:

在指定的延迟时间之后调用一个函数或执行一个代码片段。

语法

setTimeout 的语法非常简单,第一个参数为回调函数,第二个参数为延时的时间。函数返回一个数值类型的ID唯一标示符,此ID可以用作 clearTimeout 的参数来取消定时器:

var timeoutID = window.setTimeout(code, delay);

IE0+ 还支持回调参数的传入:

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

setInterval()

MDN 对 setInterval 的定义为:

周期性地调用一个函数(function)或者执行一段代码。

由于 setInterval 和 setTimeout 的用法一样,所以这里不再列出。

对第二个参数(delay)的说明

由于javascript 的事件循环机制,导致第二个参数并不代表延迟delay毫秒之后立即执行回调函数,而是尝试将回调函数加入到事件队列。实际上,setTimeout 和 setInterval 在这一点上处理又存在区别:

  • setTimeout:延时delay毫秒之后,啥也不管,直接将回调函数加入事件队列。
  • setInterval: 延时delay毫秒之后,先看看事件队列中是否存在还没有执行的回调函数(setInterval的回调函数),如果存在,就不要再往事件队列里加入回调函数了。

所以,当我们的代码中存在耗时的任务时,定时器并不会表现的如我们所想的那样。

通过一个例子来理解

下面的代码,本来希望能够在 100ms 和 200ms 的时候(也就是刚好等待 100ms)调用回调函数:

var timerStart1 = now();
setTimeout(function () {
 console.log('第一个setTimeout回调执行等待时间:', now() - timerStart1);

 var timerStart2 = now();
 setTimeout(function () {
  console.log('第二个setTimeout回调执行等待时间:', now() - timerStart2);
 }, 100);
}, 100);
// 输出:
// 第一个setTimeout回调执行等待时间: 106
// 第二个setTimeout回调执行等待时间: 107

这样的结果看上去正是我们所想的那样,但是一旦我们在代码中加入了耗时的任务时候,结果就不像我们所期望的那样了:

var timerStart1 = now();
setTimeout(function () {
 console.log('第一个setTimeout回调执行等待时间:', now() - timerStart1);

 var timerStart2 = now();
 setTimeout(function () {
  console.log('第二个setTimeout回调执行等待时间:', now() - timerStart2);
 }, 100);

 heavyTask(); // 耗时任务
}, 100);

var loopStart = now();
heavyTask(); // 耗时任务
console.log('heavyTask耗费时间:', now() - loopStart);

function heavyTask() {
 var s = now();
 while(now() - s < 1000) {
 }
}

function now () {
 return new Date();
}
// 输出:
// heavyTask耗费时间: 1015
// 第一个setTimeout回调执行等待时间: 1018
// 第二个setTimeout回调执行等待时间: 1000

两个 setTimeout 的等待事件由于耗时任务的存在不再是 100ms 了!我们来描述一下事情的经过:

  1. 首先,第一个耗时任务(heavyTask())开始执行,它需要大约 1000ms 才能执行完毕。
  2. 从耗时任务开始执行,过了 100ms, 第一个 setTimeout 的回调函数期望执行,于是被加入到事件队列,但是此时前面的耗时任务还没执行完,所以它只能在队列中等待,直到耗时任务执行完毕它才开始执行,所以结果中我们开的看到的是: 第一个setTimeout回调执行等待时间: 1018。
  3. 第一个 setTimeout 回调一执行,又开启了第二个 setTimeout, 这个定时器也是期望延时 100ms 之后能够执行它的回调函数。 但是,在第一个 setTimeout 又存在一个耗时任务,所有它的剧情跟第一个定时器一样,也等待了 1000ms 才开始执行。

可以用下面的图来概括:

javascript 定时器工作原理分析

再来看 setInterval 的一个例子:

var intervalStart = now();
setInterval(function () {
 console.log('interval距定义定时器的时间:', now() - loopStart);
}, 100);

var loopStart = now();
heavyTask();
console.log('heavyTask耗费时间:', now() - loopStart);

function heavyTask() {
 var s = now();
 while(now() - s < 1000) {
 }
}

function now () {
 return new Date();
}
// 输出:
// heavyTask耗费时间: 1013
// interval距定义定时器的时间: 1016
// interval距定义定时器的时间: 1123
// interval距定义定时器的时间: 1224

上面这段代码,我们期望每隔 100ms 就打出一条日志。相对于 setTimeout 的区别, setInterval 在准备把回调函数加入到事件队列的时候,会判断队列中是否还有未执行的回调,如果有的话,它就不会再往队列中添加回调函数。 不然,会出现多个回调同时执行的情况。

可以用下面的图来概括:

javascript 定时器工作原理分析

总结

上面对javascript定时器执行原理进行了简要的分析,希望能够帮助我们更深入的理解javascript。文中有描述不当的地方可以在评论中指出。

Javascript 相关文章推荐
Javascript &amp; DHTML 实例编程(教程)(三)初级实例篇1—上传文件控件实例
Jun 02 Javascript
初学JavaScript_03(ExtJs Grid的简单使用)
Oct 02 Javascript
javascript获取ckeditor编辑器的值(实现代码)
Nov 18 Javascript
JQuery为页面Dom元素绑定事件及解除绑定方法
Apr 23 Javascript
通过js来制作复选框的全选和不选效果
May 22 Javascript
JavaScript控制两个列表框listbox左右交换数据的方法
Mar 18 Javascript
微信小程序 网络请求(GET请求)详解
Nov 16 Javascript
基于Vue的文字跑马灯组件(npm 组件包)
May 24 Javascript
mui框架 页面无法滚动的解决方法(推荐)
Jan 25 Javascript
vue-cli 项目打包完成后运行文件路径报错问题
Jul 19 Javascript
Vue 实现拨打电话操作
Nov 16 Javascript
测量JavaScript函数的性能各种方式对比
Apr 27 Javascript
JavaScript 最佳实践:帮你提升代码质量
Dec 03 #Javascript
简单理解Vue条件渲染
Dec 03 #Javascript
学习vue.js条件渲染
Dec 03 #Javascript
浅谈jQuery中Ajax事件beforesend及各参数含义
Dec 03 #Javascript
jquery 判断div show的状态实例
Dec 03 #Javascript
利用浮层使select不可选的实现方法
Dec 03 #Javascript
textarea 在浏览器中固定大小和禁止拖动的实现方法
Dec 03 #Javascript
You might like
php下使用iconv需要注意的问题
2010/11/20 PHP
php上传文件并存储到mysql数据库的方法
2015/03/16 PHP
php实现基于pdo的事务处理方法示例
2017/07/21 PHP
ThinkPHP框架整合微信支付之Native 扫码支付模式二图文详解
2019/04/09 PHP
laravel csrf排除路由,禁止,关闭指定路由的例子
2019/10/21 PHP
FireFox中textNode分片的问题
2007/04/10 Javascript
jquery实现控制表格行高亮实例
2013/06/05 Javascript
Json实现异步请求提交评论无需跳转其他页面
2014/10/11 Javascript
AngularJS入门教程之学习环境搭建
2014/12/06 Javascript
深入理解JavaScript系列(49):Function模式(上篇)
2015/03/04 Javascript
jQuery中使用each处理json数据
2015/04/23 Javascript
Javascript递归打印Document层次关系实例分析
2015/05/15 Javascript
jQuery设计思想
2017/03/07 Javascript
详解通过源码解析Node.js中cluster模块的主要功能实现
2018/05/16 Javascript
记一次vue去除#问题处理经过小结
2019/01/24 Javascript
微信小程序实现搜索历史功能
2020/03/26 Javascript
JavaScript实现的弹出遮罩层特效经典示例【基于jQuery】
2019/07/10 jQuery
Layui弹框中数据表格中可双击选择一条数据的实现
2020/05/06 Javascript
Vue+Vant 图片上传加显示的案例
2020/11/03 Javascript
JS实现纸牌发牌动画
2021/01/19 Javascript
跟老齐学Python之关于循环的小伎俩
2014/10/02 Python
python实现下载文件的三种方法
2017/02/09 Python
Python语言描述KNN算法与Kd树
2017/12/13 Python
python之生产者消费者模型实现详解
2019/07/27 Python
django-crontab 定时执行任务方法的实现
2019/09/06 Python
Python使用正则表达式实现爬虫数据抽取
2020/08/17 Python
浅谈CSS3动画的回调处理
2016/07/21 HTML / CSS
英国第一摩托车和摩托车越野配件商店:GhostBikes
2019/03/10 全球购物
Lentiamo比利时:便宜的隐形眼镜
2020/02/14 全球购物
用C或者C++语言实现SOCKET通信
2015/02/24 面试题
退伍老兵事迹材料
2014/01/31 职场文书
小学生迎国庆演讲稿
2014/09/05 职场文书
2015年安全月活动总结
2015/03/26 职场文书
2015年销售员工作总结范文
2015/04/07 职场文书
走进科学观后感
2015/06/18 职场文书
彻底卸载VMware虚拟机的超详细步骤记录
2022/07/15 Servers