浅谈JavaScript中的定时器
引言
使用setTimeout()和setInterval()创建的定时器可以实现很多有意思的功能。很多人认为定时器是一个单独的线程(之前我也是),但是JavaScript是运行在单线程环境中的,而定时器只是计划代码在未来的某个时间执行。执行时间是不能保证的,在页面的生命周期中,会不断有其他的代码在控制着JavaScript的执行主线程。比如:在页面下载完成以后的JavaScript代码需要执行、事件处理函数、Ajax的回调函数都需要通过主线程来执行。浏览器只负责排序,指定某一段代码在某一时刻需要被执行。
下面我们通过一个例子来详细的了解下。
我们可以把JavaScript想象成在时间线上执行的。例如:页面刚加载时,首先执行的是包含在<script>元素中的代码,这通常是一些简单的函数声明和变量的初始化。在这时候,JavaScript主线程就会等待更多代码执行。当线程空闲的时候,下一段代码会被触发并立即执行。这样一个页面的时间线类似于下图:

在这张图片中,我们可以生动的看到JavaScript主线程的执行机制。其实,在JavaScript主线程之外,还有一个保存下一个即将被执行的代码的队列存在。随着页面在其生命周期中推移,代码会按照执行顺序放入队列,并在未来的某一个时间执行代码。例如:当接收到某一个Ajax响应时,其回调函数会被放入队列中。在JavaScript中是没有任务代码能立即执行的,但是一旦主线程空闲,队列中的代码会被立即执行。
定时器对于队列的工作方式是这样的:当特定时间过去后,将定时器代码插入到队列中。注意:给队列添加代码不会意味着立即执行,只是表示会尽快执行。例如:我们在定时器中设定延时200毫秒。并不是意味着200毫秒以后需代码一定会执行。只是200毫秒以后这段代码会被添加到执行队列中。如果此时执行队列为空,那么代码会被立即执行。其他情况下代码可能需要等待更长的时间来执行。
请看下面的代码:
var btn = document.getElementById("btn");
btn.onclick = function () {
setTimeout(function () {
alert("Hello");
}, 250);
//其他代码.....
}
在代码中,我们给按钮添加了事件处理程序。事件处理程序设置了一个250毫秒以后执行的定时器。点击按钮后,首先进入执行队列的是onclick事件处理函数。再过250毫秒以后,定时器的代码才会被添加到执行队列中。如果我们假设前面的onclick事件处理函数需要执行300毫秒,那么定时器代码至少需要300毫秒一以后才会执行(因为300毫秒以后JavaScript主线程执行完事件处理函数后,空闲状态,可以立即执行定时器的代码)。下面我们画一张时序图来形象的描述下这个过程。如图:

在图片中我看到,在255毫秒的时候定时器代码被添加到执行队列了。但是此时还无法运行。因为主线程在执行事件处理函数。当主线程空闲后,会立即执行定时器代码。
重复的定时器
使用setInterval定时器可以保证定时器代码规则的插入队列中。但是这是有问题的。比如:定时器代码可能在代码再次被添加到队列之前还没有执行完毕,结果导致定时器代码连续运行好几次,之间没有任何停顿。不过现代的JavaScript引擎可以避免这种问题。不过这种重复定时器的规则还是有两个弊端。
1、某些间隔会被跳过。
2、多个定时器的代码执行之间的间隔可能比预期的小。
假设某一个事件处理程序使用setInterval设置了一个200毫秒的重复定时器。如果事件处理程序需要300多毫秒才执行完毕,同时定时器代码页花了差不多时间,那么就会跳过一个定时器间隔,因为前一个定时器的代码还没有执行完毕。请看下图:

我们看这张图,我们看到在5毫秒的时候我们创建了重复的定时器,在205毫秒的时候,队列中添加了第一个定时器代码。但是事件处理程序需要300多毫秒才会执行完毕。事件处理函数执行完毕以后,主线程立即执行队列中的定时器代码。在400多毫秒的时候,第二个定时器代码被添加到队列中。但是单到了600多毫秒时,第三个定时器代码会被跳过,因为当前执行队列存在未执行的代码(第二个定时器代码)。
解决方案
setTimeout(function(){
//处理代码
setTimeout(argument.callee,interval);
},interval);
在这段代码中,我们使用了链式调用setTimeout()模式。每一次函数调用都会重新创建一个新的定时器。第二个setTimeout函数使用了argument.callee来获取当前执行函数的引用,并且设置了另一个新的定时器。这样做的好处是:在前一个定时器代码执行之前,不会往队列中添加新的定时器代码,不会存在任何间隔。同时也可以保证在下一次定时器代码执行之前,至少需要等待一段时间(interval的值),避免了连续执行。
浅谈JavaScript中的定时器的更多相关文章
- 浅谈JavaScript中的闭包
浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...
- 浅谈JavaScript中的null和undefined
浅谈JavaScript中的null和undefined null null是JavaScript中的关键字,表示一个特殊值,常用来描述"空值". 对null进行typeof类型运 ...
- 浅谈JavaScript中的正则表达式(适用初学者观看)
浅谈JavaScript中的正则表达式 1.什么是正则表达式(RegExp)? 官方定义: 正则表达式是一种特殊的字符串模式,用于匹配一组字符串,就好比用模具做产品,而正则就是这个模具,定义一种规则去 ...
- 浅谈JavaScript中的内存管理
一门语言的内存存储方式是我们学习他必须要了解的,接下来让我浅谈一下自己对他的认识. 首先说,JavaScript中的变量包含两种两种类型: 1)值类型或基本类型:undefined.null.numb ...
- 通过一道笔试题浅谈javascript中的promise对象
因为前几天做了一个promise对象捕获错误的面试题目,所以这几天又重温了一下promise对象.现在借这道题来分享下一些很基础的知识点. 下面是一个面试题目,三个promise对象捕获错误的例子,返 ...
- 浅谈JavaScript中闭包
引言 闭包可以说是JavaScript中最有特色的一个地方,很好的理解闭包是更深层次的学习JavaScript的基础.这篇文章我们就来简单的谈下JavaScript下的闭包. 闭包是什么? 闭包是什么 ...
- 浅谈JavaScript中的继承
引言 在JavaScript中,实现继承的主要方式是通过原型链技术.这一篇文章我们就通过介绍JavaScript中实现继承的几种方式来慢慢领会JavaScript中继承实现的点点滴滴. 原型链介绍 原 ...
- 浅谈JavaScript中继承的实现
谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我 ...
- 【总结】浅谈JavaScript中的接口
一.什么是接口 接口是面向对象JavaScript程序员的工具箱中最有用的工具之一.在设计模式中提出的可重用的面向对象设计的原则之一就是“针对接口编程而不是实现编程”,即我们所说的面向接口编程,这个概 ...
随机推荐
- UNIX命令,统计当前目录(含子目录)下所有后缀为.log的文件中ERROR出现的行数
shell程序如下所示: # cat xarg.txt #! /usr/bin/ksh for logfile in `find . -name "*.log*"` do echo ...
- PHP获取MySql新增记录ID值的方法
今天发现用mysql_insert_id()获取到的新增记录的id不正确, 虽然发现源代码的事务写的有问题,但是最根本的原因是,我插入数据的id类型是bigint型 获取MySql新增记录ID值的方法 ...
- BLOG搬家
前段时间一直用的新浪blog,发现推送信息是否杂乱,所以特地投奔学长CSDN,外加之前颓废心过重,现在打算新面貌认真学习
- MAC上快速调出终端的设置(保持和Windows的操作一致)
在Windows上可以这样操作[Win+R]键->输入[cmd/cmder]打开终端. 在MAC下需要做些设置:打开[系统偏好设置]->打开[键盘]->打开[快捷键]->找到[ ...
- CodeForces 125E MST Company
E. MST Company time limit per test 8 seconds memory limit per test 256 megabytes input standard inpu ...
- Java Attach API
catalog . instrucment与Attach API . BTrace: VM Attach的两种方式 . Sun JVM Attach API 1. instrucment与Attach ...
- PHP serialize && unserialize Security Risk Research
目录 . 序列化的定义 . serialize:序列化 . unserialize:反序列化 . 序列化.反序列化存在的安全风险 . Use After Free Vulnerability -] . ...
- [PHP 作为iOS后台Json格式HTTP通信及文件上传的实现]
1.数据库连接 configmysql.php <?php $q = mysql_connect("localhost:8889","root",&quo ...
- python模块
模块介绍 为什么用模块 在代码量庞大的时候,如果靠一个脚本来进行函数式编程,对于脚本的可读性及后期脚本的维护带来极大的不便,而且无法分辨函数间的调用关系,所以,可以将一组功能类似的函数单独放在一个py ...
- UVa 437 The Tower of Babylon(经典动态规划)
传送门 Description Perhaps you have heard of the legend of the Tower of Babylon. Nowadays many details ...