使用 setTimeout()和 setInterval()创建的定时器可以用于实现有趣且有用的功能。执行时机是不能保证的,因为在页面的生命周期中,不同时间可能有其他代码在控制 JavaScript 进程。在页面下载完后的代码运行、事件处理程序、Ajax 回调函数都必须使用同样的线程来执行。实际上,浏览器负责进行排序,指派某段代码在某个时间点运行的优先级。有关于js的单线程请点击:浏览器的多进程
可以把 JavaScript 想象成在时间线上运行的。当页面载入时,首先执行是任何包含在<script>元素中的代码,通常是页面生命周期后面要用到的一些简单的函数和变量的声明,不过有时候也包含一些初始数据的处理。在这之后,JavaScript 主线程将等待更多代码执行。例如,当点击某个按钮时,onclick 事件处理程序会立刻执行,只要 JavaScript 主线程处于空闲状态。这样一个页面的时间线类似如图
除了JavaScript 主线程外,还有一个需要在进程下一次空闲时执行的代码队列。随着页面在其生命周期中的推移,代码会按照执行顺序添加入队列。例如,当某个按钮被按下时,它的事件处理程序代码就会被添加到队列中,并在下一个可能的时间里执行。当接收到某个 Ajax 响应时,回调函数的代码会被添加到队列。在 JavaScript 中没有任何代码是立刻执行的,但一旦主线程空闲则尽快执行。
 
定时器对队列的工作方式是,当特定时间过去后将代码插入。注意,给队列添加代码并不意味着对它立刻执行,而只能表示它会尽快执行。设定一个 150ms 后执行的定时器不代表到了 150ms 代码就立刻执行,它表示代码会在 150ms 后被加入到队列中。如果在这个时间点上,队列中没有其他东西,那么这段代码就会被执行,表面上看上去好像代码就在精确指定的时间点上执行了。其他情况下,代码可能明显地等待更长时间才执行。
请看以下代码:
var btn = document.getElementById("my-btn");
btn.onclick = function(){
setTimeout(function(){
document.getElementById("message").style.visibility = "visible";
}, 250);
//其他代码
};
在这里给一个按钮设置了一个事件处理程序。事件处理程序设置了一个 250ms 后调用的定时器。点击该按钮后,首先将 onclick 事件处理程序加入队列。该程序执行后才设置定时器,再有 250ms 后,指定的代码才被添加到队列中等待执行。实际上,对 setTimeout()的调用表示要晚点执行某些代码。
 
关于定时器要记住的最重要的事情是,指定的时间间隔表示何时将定时器的代码添加到队列,而不是何时实际执行代码。如果前面例子中的 onclick 事件处理程序执行了 300ms,那么定时器的代码至少要在定时器设置之后的 300ms 后才会被执行。队列中所有的代码都要等到 JavaScript 进程空闲之后才能执行,而不管它们是如何添加到队列中的。如图
 
 
重复的定时器
 
使用 setInterval()创建的定时器确保了定时器代码规则地插入队列中。这个方式的问题在于,定时器代码可能在代码再次被添加到队列之前还没有完成执行,结果导致定时器代码连续运行好几次,而之间没有任何停顿。幸好,JavaScript 引擎够聪明,能避免这个问题。当使用 setInterval()时,仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中。这确保了定时器代码加入到队列中的最小时间间隔为指定间隔。
 
这种重复定时器的规则有两个问题:(1) 某些间隔会被跳过;(2) 多个定时器的代码执行之间的间隔可能会比预期的小。假设,某个 onclick 事件处理程序使用 setInterval()设置了一个 200ms 间隔的重复定时器。如果事件处理程序花了 300ms 多一点的时间完成,同时定时器代码也花了差不多的时间,就会同时出现跳过间隔且连续运行定时器代码的情况。
这个例子中的第 1 个定时器是在 205ms 处添加到队列中的,但是直到过了 300ms 处才能够执行。当执行这个定时器代码时,在 405ms 处又给队列添加了另外一个副本。在下一个间隔,即 605ms 处,第一个定时器代码仍在运行,同时在队列中已经有了一个定时器代码的实例。结果是,在这个时间点上的定时器代码不会被添加到队列中。结果在 5ms 处添加的定时器代码结束之后,405ms 处添加的定时器代码就立刻执行。
为了避免 setInterval()的重复定时器的这 2 个缺点,你可以用如下模式使用链式 setTimeout() 调用。
setTimeout(function(){
//处理中
setTimeout(arguments.callee, interval);
}, interval);
 
这个模式链式调用了 setTimeout(),每次函数执行的时候都会创建一个新的定时器。第二个setTimeout()调用使用了 arguments.callee 来获取对当前执行的函数的引用,并为其设置另外一个定时器。这样做的好处是,在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。这个模式主要用于重复定时器,如下例所示。
setTimeout(function(){
var div = document.getElementById("myDiv");
left = parseInt(div.style.left) + 5;
div.style.left = left + "px";
if (left < 200){
setTimeout(arguments.callee, 50);
}
}, 50);
这段定时器代码每次执行的时候将一个<div>元素向右移动,当左坐标在 200 像素的时候停止。
JavaScript 动画中使用这个模式很常见。
本文转载自:javascript高级程序设计(第3版)

高级定时器-setTimeout()、setInterval()、链式setTimeout()的更多相关文章

  1. 让setTimeout支持链式

    修改很简单,通过参数判断,然后返回下promise对象 (function() { var timeout = setTimeout; window.setTimeout = function(fn, ...

  2. 深入理解定时器系列第一篇——理解setTimeout和setInterval

    × 目录 [1]setTimeout [2]setInterval [3]运行机制[4]作用[5]应用 前面的话 很长时间以来,定时器一直是javascript动画的核心技术.但是,关于定时器,人们通 ...

  3. js中setTimeout/setInterval定时器用法示例

    js中setTimeout(定时执行一次)和setInterval(间隔循环执行)用法介绍. setTimeout:在指定的毫秒数后调用指定的代码段或函数:setTimeout示例代码 functio ...

  4. js高级技巧之高级定时器

    实际上,浏览器负责进行排序,指派某段代码在某个时间点运行的优先级. 可以吧js想象成在时间线上运行的. JavaScript中没有任何代码是立刻执行的,但一旦进程空闲则尽快执行. 1.重复的定时器: ...

  5. 关于JavaScript中的setTimeout()链式调用和setInterval()探索

    http://www.cnblogs.com/Wenwang/archive/2012/01/06/2314283.html http://www.cnblogs.com/yangjunhua/arc ...

  6. 定时器相关 setTimeout setInterval 函数节流

    这个问题也是在参加百度的前端技术学院中遇到的 任务中需要用js实现动画  导师给的评价中setInterval会导致bug 当时不理解   下面把自己学习的过程分享出来 再次理解单线程   老是说js ...

  7. setTimeout setInterval 区别 javascript线程解释

    原文:http://www.iamued.com/qianduan/1645.html 今天看到这篇文章,学到了不少东西 特此发出来 和大家分享 JavaScript的setTimeout与setIn ...

  8. setTimeout/setInterval执行时机

    setTimeout()和setInterval()经常被用来处理延时和定时任务.setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式,而setInterval()则可以在每隔指定的 ...

  9. javascript线程解释(setTimeout,setInterval你不知道的事)

    john resig写的一篇文章: 原文地址:http://ejohn.org/blog/how-javascript-timers-work/ 作为入门者来说,了解JavaScript中timer的 ...

随机推荐

  1. 在MySQL登录时出现Access denied for user 'root'@'localhost' (using password: YES) 拒绝访问,并可修改MySQL密码

    这两天装了一个mysql 5.7数据库,但是在本地dos登陆不上去,报错Access denied for user 'root'@'localhost'(using password:YES): 也 ...

  2. HTML5 中list 和datalist实例

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  3. element-ui 里面el-checkbox多选框,实现全选单选

    data里面定义了 data:[],        actionids:[],//选择的那个actionid        num1:0,//没选择的计数        num2:0,//选中的计数  ...

  4. Callable使用示例

    之前工作中也有使用过Callable,但是却没有使用Thread对象去操作过,今晚 小组培训,涨知识了,现特意记录一下,以免忘记. 先看一下Thread的构造器 可以看到,Thread类并没有提供参数 ...

  5. 本页面用来演示如何通过JS SDK,创建完整的QQ登录流程,并调用openapi接口

    QQ登录将用户信息存储在cookie中,命名为__qc__k ,请不要占用 __qc__k : 1) :: 在页面顶部引入JS SDK库: 将“js?”后面的appid参数(示例代码中的:100229 ...

  6. 如何扩展 Create React App 的 Webpack 配置

    如何扩展 Create React App 的 Webpack 配置  原文地址https://zhaozhiming.github.io/blog/2018/01/08/create-react-a ...

  7. 12 October

    次小生成树 http://poj.org/problem?id=1679 不难得出,次小生成树可以由最小生成树更换一条边得到. 首先构造原图的最小生成树,然后枚举每一条不在最小生成树中的边 (u, v ...

  8. 笨办法学Python(learn python the hard way)--练习程序39-40

    下面是练习39-练习40,基于python3 #ex39.py 1 ten_things = "Apples Oranges Crows Telephone Light Sugar" ...

  9. 如何选择Linux操作系统版本?

    一般来讲, 桌面用户首选Ubuntu; 服务器首选RHEL或CentOS, 两者中首选CentOS; 根据具体要求: 1.安全性要求较高, 则选择Debian或者FreeBSD. 2.需要要使用数据库 ...

  10. ArayList的一些常用法<一> 转

    转:http://yy.azj.blog.163.com/blog/static/18508700320122893451389/ import java.util.ArrayList; import ...