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 相关文章推荐
cookie的复制与使用记住用户名实现代码
Nov 04 Javascript
邮箱下拉自动填充选择示例代码附图
Apr 03 Javascript
javascript实现倒计时并弹窗提示特效
Jun 05 Javascript
Bootstrap学习笔记之js组件(4)
Jun 12 Javascript
jQuery插件dataTables添加序号列的方法
Jul 06 Javascript
js删除Array数组中指定元素的两种方法
Aug 03 Javascript
easyui导出excel无法弹出下载框的快速解决方法
Nov 10 Javascript
BootstrapTable请求数据时设置超时(timeout)的方法
Jan 22 Javascript
jQuery插件echarts实现的多折线图效果示例【附demo源码下载】
Mar 04 Javascript
js实现关闭网页出现是否离开提示
Dec 07 Javascript
在create-react-app中使用css modules的示例代码
Jul 31 Javascript
使用konva和vue-konva库实现拖拽滑块验证功能
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.ini中的php-5.2.0配置指令详解
2008/03/27 PHP
CodeIgniter配置之autoload.php自动加载用法分析
2016/01/20 PHP
[原创]php token使用与验证示例【测试可用】
2017/08/30 PHP
javascript延时重复执行函数 lLoopRun.js
2007/06/29 Javascript
初窥JQuery(二)事件机制(2)
2010/12/06 Javascript
JavaScrip实现PHP print_r的数功能(三种方法)
2013/11/12 Javascript
jquery获取复选框被选中的值
2014/03/22 Javascript
jquery实现的图片点击滚动效果
2014/04/29 Javascript
jQuery实现字符串按指定长度加入特定内容的方法
2015/03/11 Javascript
详解js中Number()、parseInt()和parseFloat()的区别
2016/12/20 Javascript
js上传图片预览的实现方法
2017/05/09 Javascript
JavaScript实现设置默认日期范围为最近40天的方法分析
2017/07/12 Javascript
微信小程序scroll-x失效的完美解决方法
2018/07/18 Javascript
bootstrap模态框关闭后清除模态框的数据方法
2018/08/10 Javascript
vue-cli3+typescript初体验小结
2019/02/28 Javascript
node(koa2) web应用模块介绍详解
2019/03/29 Javascript
抖音上用记事本编写爱心小程序教程
2019/04/17 Javascript
[40:13]Ti4 冒泡赛第二天 iG vs NEWBEE 2
2014/07/15 DOTA
[01:26]神话结束了,却也刚刚开始——DOTA2新英雄玛尔斯驾临战场
2019/03/10 DOTA
python3.0 字典key排序
2008/12/24 Python
使用python实现递归版汉诺塔示例(汉诺塔递归算法)
2014/04/08 Python
Python通过RabbitMQ服务器实现交换机功能的实例教程
2016/06/29 Python
python交互式图形编程实例(三)
2017/11/17 Python
tensorflow1.0学习之模型的保存与恢复(Saver)
2018/04/23 Python
python 限制函数执行时间,自己实现timeout的实例
2019/01/12 Python
python+pyqt5实现24点小游戏
2019/01/24 Python
python写一个随机点名软件的实例
2019/11/28 Python
python3实现网页版raspberry pi(树莓派)小车控制
2020/02/12 Python
使用Python开发个京东上抢口罩的小实例(仅作技术研究学习使用)
2020/03/10 Python
全网首秀之Pycharm十大实用技巧(推荐)
2020/04/27 Python
浅谈html5与APP混合开发遇到的问题总结
2018/03/20 HTML / CSS
青蓝工程实施方案
2014/03/27 职场文书
廉洁自律承诺书2015
2015/01/22 职场文书
大学生毕业个人总结
2015/02/15 职场文书
个人求职自荐信范文
2015/03/06 职场文书
游戏开发中如何使用CocosCreator进行音效处理
2021/04/14 Javascript