当前位置: 首页 > news >正文

理解JavaScript定时器:setTimeout和setInterval

定时器其实并不是JavaScript提供的,而是由浏览器(对于前端来说)提供的。所以setTimeout()setInterval()这两个方法均是通过浏览器的顶层对象window进行调用,可能平时大家在使用的过程中也会省去window而直接使用这两个方法。

这两个方法所接收的参数都一样:

 

1
2
setTimeout(func|code, delay);
setInterval(func|code, delay);


这两个方法总是被简单的认为:在多少毫秒之后就执行里面的函数或者每间隔多少毫秒就执行里面的函数,基于这种理解的话会遇到很多匪夷所思的坑。而结合上篇文章中所提到的执行队列来解释的话,很多疑问都可以迎刃而解。

 

前者:在指定的毫秒数后,将定时任务处理函数(func|code)添加到执行队列的队尾。

后者:按照指定的周期(以毫秒计),将定时任务处理函数(func|code)添加到执行队列的队尾。
下面分别使用了setIntervalsetTimeout来实现同一个功能,可运行查看效果。

这是相应的源代码:传送门

接下来继续填setInterval的坑。

假设定时器的上一个回调执行完到下一个回调开始的这段时间为时间间隔,那么对于setTimeout来说,这个时间间隔理论上是应该>=delay;而对于setInterval来说,这个时间间隔理论上是应该<=delay的。

但事实总会有出人意料的地方,setInterval就是那个制造意外的东西。

以下是常规的代码:

 

1
2
3
4
5
6
7
8
var endTime = null;
setInterval(count, 200);
function count() {
  var elapsedTime = endTime ? (new Date() - endTime) : 200;
  i++;
  console.log('current count: ' + i + '.' + 'elapsed time: ' + elapsedTime + 'ms');
  endTime = new Date();
}


其执行结果也比较符合理论时间,见下图。

 

接下来修改代码,让count()方法的执行时间变长一点:

 

1
2
3
4
5
6
7
function count() {
  var elapsedTime = endTime ? (new Date() - endTime) : 200;
  i++;
  console.log('current count: ' + i + '.' + 'elapsed time: ' + elapsedTime + 'ms');
  sleep(100); //sleep 100ms
  endTime = new Date();
}

执行结果如下:

 

结合执行队列,可以用下图对上面两种情况进行直观的解释:

接下来再次修改代码,让count()方法的执行时间更长,设定为setIntervaldelay2倍,即400ms

 

1
2
3
4
5
6
7
function count() {
  var elapsedTime = endTime ? (new Date() - endTime) : 200;
  i++;
  console.log('current count: ' + i + '.' + 'elapsed time: ' + elapsedTime + 'ms');
  sleep(400); //sleep 400ms
  endTime = new Date();
}


其执行效果变为如下:

 

意外发生了,每个回调之间的时间间隔竟然没有了,或者说缩短到非常小的间隔。事情大概是这样的:如果setInterval的定时时间到了,而前一个回调还没有执行完时,就会把这次的回调放在执行队列的队尾;如果setInterval的定时时间已经多次触发,而此时最前一个回调仍然还在执行,那么就会丢弃掉本次的回调。还是用图来直观说明吧。

这是回调处理时间比定时时间稍微长一点点的情况:

这是回调处理时间比定时时间长很多的情况:

所以,如果使用setInterval的话,其时间间隔总是让人捉摸不定。而使用setTimeout嵌套,则完全可以解决这个问题,还我们一个固定的时间间隔。

相关文章:

  • 13、jQueryMobile知识总结
  • 控件函数对话框上的控件的大小和位置随着对话框的大小的改变而变化
  • 线段树(多棵) HDOJ 4288 Coder
  • linux基础1
  • windons 安装ruby on rails
  • ChannelHandler adapters
  • 设置MySQL开机自动启动
  • [svc][op]关闭linux centos各种声音
  • unity3d倒计时后几秒改变颜色方法
  • js 多语言转换代码
  • HtmlUnit、httpclient、jsoup爬取网页信息并解析
  • 10个你可能没用过的Linux命令
  • 备案以及端口
  • gjrand 4.0.2 发布,伪随机数生成器
  • 必须掌握的八种排序(3-4)--简单选择排序,堆排序
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • 30秒的PHP代码片段(1)数组 - Array
  • 4. 路由到控制器 - Laravel从零开始教程
  • ES6 学习笔记(一)let,const和解构赋值
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • Joomla 2.x, 3.x useful code cheatsheet
  • learning koa2.x
  • Linux快速复制或删除大量小文件
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • socket.io+express实现聊天室的思考(三)
  • Spring Cloud Feign的两种使用姿势
  • supervisor 永不挂掉的进程 安装以及使用
  • Twitter赢在开放,三年创造奇迹
  • Vultr 教程目录
  • 记一次用 NodeJs 实现模拟登录的思路
  • 理解在java “”i=i++;”所发生的事情
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 项目实战-Api的解决方案
  • 赢得Docker挑战最佳实践
  • 字符串匹配基础上
  • 阿里云重庆大学大数据训练营落地分享
  • 如何在招聘中考核.NET架构师
  • #162 (Div. 2)
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (分类)KNN算法- 参数调优
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (已解决)什么是vue导航守卫
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)大型网站的系统架构
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • ****Linux下Mysql的安装和配置
  • .NET Core 成都线下面基会拉开序幕
  • .Net IOC框架入门之一 Unity
  • .net MVC中使用angularJs刷新页面数据列表
  • .NET导入Excel数据
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件