掌握定时器工作原理必知:JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序. 常言道:setTimeout和setInterval是伪线程。

  Javascript是运行在单线程环境中的,在页面的声明周期中,不同时间可能有其他代码在控制Javascript进程,比如:包含在<script>元素中的代码、dom元素的事件处理程序、Ajax的回调函数。定时器仅仅是在未来的某个时刻将代码添加到代码队列中,执行时机是不能保证的。代码队列按照先进先出的原则在主进程空闲后将队列中的代码交给主线程运行。

  

  在不同时间段内Javascript主进程处于不同状态,开始时执行页面中<script>元素内的代码,初始加载完成后,主进程进入空闲状态,这时候有dom元素产生click事件,事件处理代码被添加到代码队列中,代码队列发现Javascript主进程处于空闲状态,立即将队列中的第一个元素交给主进程执行。上图便是这一个过程的时间线。

  在Javascript中没有任何代码是立刻执行的,带一旦进程空闲则尽快执行。例如,当某个按钮被按下时,事件处理函数会被添加到代码队列中。当接收到ajax响应时,回校函数的代码被添加到队列中。而定时器对队列的工作方式是,当特定的事件过去后将代码加入到队列中。设定一个150ms后执行的定时器不代表代码会在150ms之后执行,而是指代码会在150ms后加入到代码队列中。等到主进程空闲时并且该元素位于队列首位,其中的代码便会立即执行,看上去好像是在精确的时间点上执行了。实际上队列中的所有代码都要等到主进程空闲之后才能执行,而不管他们是怎额添加到队列中去的。

var ele = document.getElementById('btn');
ele.onclick = function(){
setTimeout(function(){
document.getElementById(message).style.backgroundColor = "red";
}, 255);
var start = Date.now();
while(Date.now() - start < 300) {};
}

  以上示例中,定时器在255ms事被插入到代码队列中,但Javascript主线程有300ms处于运行状态,那么定时器代码至少要在定时器设置之后的300ms后才会被执行。以下时间线代表了上面代码的执行过程。

  

  重复定时器setInterval

  为了确保定时器代码插入到队列总的最小间隔为指定时间。当使用setInterval()时,仅当没有该定时器的任何其他代码实例时,才能将定时器代码添加到代码队列中。假设没有这条原则,setInterval()创建的定时器确保了定时器代码能够规则的插入队列中。那么问题来了,假设Javascript主进程的运行时间非常长,那么setInterval的代码被多次添加到了代码队列中,等到主进程空闲时,定时器代码便会连续执行多次而之间不会有任何停顿。

  但是这条规则同样也带来了两个问题:

  1. 某些间隔会被跳过
  2. 多个定时器的代码执行之间的间隔可能会比预期的小
var ele = document.getElementById('btn');
ele.onclick = function(){
setInterval(function(){
    console.log('run interval');
var start = Date.now();
while(Date.now() - start < 350) {};
}, 200);
var start = Date.now();
while(Date.now() - start < 300) {};
}

  以上代码中,click事件处理程序中通过setInterval设置了一个200ms的时间间隔的重复定时器。从以上代码可以看出事件处理程序花了300ms多的时间完成,而定时器代码也花了300ms的时间,这个时候就会出现跳过间隔且连续两次运行定时器代码的情况。请看下图:

  

  上图中,第一个定时器在205ms出添加到队列中的,但是直到过了300ms才能够执行。当执行定时器代码时,在405ms时有一个定时器代码实例被添加到等待队列中。在605ms处第一个定时器代码仍然在运行,同时在代码队列中已经有了另一个定时器的代码实例。所以在这个时间点上的定时器代码不会被添加到队列中。结果在205ms处添加的定时器代码执行完毕后,405ms处添加的定时器代码会立即执行。

  所以在使用setInterval做动画时要注意两个问题:

  1. 不能使用固定步长作为做动画,一定要使用百分比: 开始值 + (目标值 - 开始值) * (Date.now() - 开始时间)/ 时间区间
  2. 如果主进程运行时间过长,会出现跳帧的现象

  为了避免setInterval的两个缺点,可以使用链式setTimeout():

setTimeout(function(){
//其他处理
setTimeout(arguments.callee, interval);
}, interval);

  以上文章内容主要来自《Javascript高级程序设计》

Javascript定时器学习笔记的更多相关文章

  1. JavaScript正则表达式学习笔记(二) - 打怪升级

    本文接上篇,基础部分相对薄弱的同学请移步<JavaScript正则表达式学习笔记(一) - 理论基础>.上文介绍了8种JavaScript正则表达式的属性,本文还会追加介绍几种JavaSc ...

  2. javascript正则表达式 - 学习笔记

    JavaScript 正则表达式 学习笔记 标签(空格分隔): 基础 JavaScript 正则表达式是用于匹配字符串中字符组合的模式.在javascript中,正则表达式也是对象.这些模式被用于Re ...

  3. JavaScript简易学习笔记

    学习地址:http://www.w3school.com.cn/js/index.asp 文字版: https://github.com/songzhenhua/github/blob/master/ ...

  4. javaScript 对象学习笔记

    javaScript 对象学习笔记 关于对象,这对我们软件工程到学生来说是不陌生的. 因为这个内容是在过年学到,事儿多,断断续续,总感觉有一丝不顺畅,但总结还是要写一下的 JavaScript 对象 ...

  5. 【MarkMark学习笔记学习笔记】javascript/js 学习笔记

    1.0, 概述.JavaScript是ECMAScript的实现之一 2.0,在HTML中使用JavaScript. 2.1 3.0,基本概念 3.1,ECMAScript中的一切(变量,函数名,操作 ...

  6. Javascript MVC 学习笔记(一) 模型和数据

    写在前面 近期在看<MVC的Javascript富应用开发>一书.本来是抱着一口气读完的想法去看的.结果才看了一点就傻眼了:太多不懂的地方了. 仅仅好看一点查一点,一点一点往下看吧,进度虽 ...

  7. Javascript作用域学习笔记(三)

    看完<你不知道的javascript>上,对作用域的新的理解(2018-9-25更) 一.学习笔记:   1.javascript中的作用域和作用域链 +  每个函数在被调用时都会创建一个 ...

  8. Javascript - Promise学习笔记

    最近工作轻松了点,想起了以前总是看到的一个单词promise,于是耐心下来学习了一下.   一:Promise是什么?为什么会有这个东西? 首先说明,Promise是为了解决javascript异步编 ...

  9. JavaScript闭包学习笔记

    此文都是大牛们关于闭包的观点,在此只是总结. 闭包应用的两种情况即可——函数作为返回值,函数作为参数传递. 1 深入理解javascript原型和闭包 判断一个变量是不是对象非常简单.值类型的类型判断 ...

随机推荐

  1. js--webSocket入门

    Websocket 1.websocket是什么? WebSocket是为解决客户端与服务端实时通信而产生的技术.其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接, 此 ...

  2. XE3随想14:关于 SO 与 SA 函数

    通过 SuperObject 的公用函数 SO 实现一个 ISuperObject 接口非常方便; 前面都是给它一个字符串参数, 它的参数可以是任一类型甚至是常数数组. SA 和 SO 都是返回一 I ...

  3. 我利用网上代码开发的JQuery图片插件

    我利用网上代码开发的JQuery图片插件 代码如下 (function($){ $.fn.FocusPic = function(options){ var defaults = { interval ...

  4. Ceph剖析:Paxos算法实现

    作者:吴香伟 发表于 2014/10/8 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 Recovery阶段 在Leader选举成功后,Leader和Peon都 ...

  5. Shell使用技巧

    巧用定界符delimiter 输入大段文本的地方 自动选择和输入的地方 访问数据库 Angel@DESKTOP-254LBLA ~ $ cat > out.txt << EOF &g ...

  6. Java Web2

    JavaBean组件. 定义方法:(假设有一个JavaBean的类名为CounterBean,它有一个count属性.) //在JSP文件中分别定义4种范围内的JavaBean对象的语法 //in p ...

  7. CSS自适应布局(包括两边宽度固定中间宽度自适应与中间宽度固定两边宽度自适应)

    1.两边宽度固定,中间宽度自适应 (1)非CSS3布局,浮动定位都可以(以下用浮动) css样式: #left { float: left;width: 200px; background: lime ...

  8. js 正则验证输入框只允许输入正实数和正整数和负整数

    <input onkeyup="this.value=this.value.replace(/[^0-9.]/g,'')">  (正实数) <input onke ...

  9. 关于js单页面实现跳转原理以及利用angularjs框架路由实现单页面跳转

    还记得我们刚开始学习html时使用的锚节点实现跳转吗? <a href="#target">我想跳转至目标位置</a> <p>第一条</p ...

  10. JS-字符串操作-替换

    <!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content=&q ...