定义

setTimeout()和setInterval()经常被用来处理延时和定时任务。setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式,而setInterval()则可以在每隔指定的毫秒数循环调用函数或表达式,直到clearInterval把它清除。

从定义上我们可以看到两个函数十分类似,只不过前者执行一次,而后者可以执行多次,两个函数的参数也相同,第一个参数是要执行的code或句柄,第二个是延迟的毫秒数。

很简单的定义,使用起来也很简单,但有时候我们的代码并不是按照我们的想象精确时间被调用的,很让人困惑

简单示例

看个简单的例子,简单页面在加载完两秒后,写下Delayed alert!

  1. setTimeout('document.write("Delayed alert!");', 2000);

看起来很合理,我们再看个setInterVal()方法的例子

  1. var num = 0;
  2. var i = setInterval(function() {
  3. num++;
  4. var date = new Date();
  5. document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
  6. if (num > 10)
  7. clearInterval(i);
  8. }, 1000);

页面每隔1秒记录一次当前时间(分钟:秒:毫秒),记录十次后清除,不再记录。考虑到代码执行时间可能记录的不是执行时间,但时间间隔应该是一样的,看看结果

  1. 43:38:116
  2. 43:39:130
  3. 43:40:144
  4. 43:41:158
  5. 43:42:172
  6. 43:43:186
  7. 43:44:200
  8. 43:45:214
  9. 43:46:228
  10. 43:47:242
  11. 43:48:256

为什么

时间间隔几乎是1000毫秒,但不精确,这是为什么呢?原因在于我们对JavaScript定时器存在一个误解,JavaScript其实是运行在单线程的环境中的,这就意味着定时器仅仅是计划代码在未来的某个时间执行,而具体执行时机是不能保证的,因为页面的生命周期中,不同时间可能有其他代码在控制JavaScript进程。在页面下载完成后代码的运行、事件处理程序、Ajax回调函数都是使用同样的线程,实际上浏览器负责进行排序,指派某段程序在某个时间点运行的优先级

我们把效果放大一下看看,添加一个耗时的任务

  1. function test() {
  2. for (var i = 0; i < 500000; i++) {
  3. var div = document.createElement('div');
  4. div.setAttribute('id', 'testDiv');
  5. document.body.appendChild(div);
  6. document.body.removeChild(div);
  7. }
  8. }
  9. setInterval(test, 10);
  10. var num = 0;
  11. var i = setInterval(function() {
  12. num++;
  13. var date = new Date();
  14. document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
  15. if (num > 10)
  16. clearInterval(i);
  17. }, 1000);

我们又加入了一个定时任务,看看结果

  1. 47:9:222
  2. 47:12:482
  3. 47:16:8
  4. 47:19:143
  5. 47:22:631
  6. 47:25:888
  7. 47:28:712
  8. 47:32:381
  9. 47:34:146
  10. 47:35:565
  11. 47:37:406

这下效果明显了,差距甚至都超过了3秒,而且差距很不一致。

我们可以可以把JavaScript想象成在时间线上运行。当页面载入的时候首先执行的是页面生命周期后面要用的方法和变量声明和数据处理,在这之后JavaScript进程将等待更多代码执行。当进程空闲的时候,下一段代码会被触发

除了主JavaScript进程外,还需要一个在进程下一次空闲时执行的代码队列。随着页面生命周期推移,代码会按照执行顺序添加入队列,例如当按钮被按下的时候他的事件处理程序会被添加到队列中,并在下一个可能时间内执行。在接到某个Ajax响应时,回调函数的代码会被添加到队列。JavaScript中没有任何代码是立即执行的,但一旦进程空闲则尽快执行。定时器对队列的工作方式是当特定时间过去后将代码插入,这并不意味着它会马上执行,只能表示它尽快执行。

知道了这些后,我们就能明白,如果想要精确的时间控制,是不能依赖于JavaScript的setTimeout函数的。

重复的定时器

使用 setInterval() 创建的定时器可以使代码循环执行,到有指定效果的时候,清除interval就可以,如下例

  1. var my_interval = setInterval(function () {
  2. if (condition) {
  3. //..........
  4. } else {
  5. clearInterval(my_interval);
  6. }
  7. }, 100);

但这个方式的问题在于定时器的代码可能在代码再次被添加到队列之前还没有执行完成,结果导致循环内的判断条件不准确,代码多执行几次,之间没有停顿。不过JavaScript已经解决这个问题,当使用setInterval()时,仅当没有该定时器的其他代码实例时才将定时器代码插入队列。这样确保了定时器代码加入到队列的最小时间间隔为指定间隔。

这样的规则带来两个问题

  1. 1. 某些间隔会被跳过
  2. 2.多个定时器的代码执行之间的间隔可能比预期要小

为了避免这两个缺点,我们可以使用setTimeout()来实现重复的定时器

  1. setTimeout(function () {
  2. //code
  3. setTimeout(arguments.callee, interval);
  4. }, interval)

这样每次函数执行的时候都会创建一个新的定时器,第二个setTimeout()调用使用了agrument.callee 来获取当前实行函数的引用,并设置另外一个新定时器。这样做可以保证在代码执行完成前不会有新的定时器插入,并且下一次定时器代码执行之前至少要间隔指定时间,避免连续运行。

  1. setTimeout(function () {
  2. var div = document.getElementById('moveDiv');
  3. var left = parseInt(div.style.left) + 5;
  4. div.style.left = left + 'px';
  5. if (left < 200) {
  6. setTimeout(arguments.callee, 50);
  7. }
  8. }, 50);

这段定时器代码每次执行的时候,把一个div向右移动5px,当坐标大于200的时候停止。

文转:http://www.cnblogs.com/dolphinX/archive/2013/04/05/2784933.html

setTimeout()和setInterval() 何时被调用执行(非多线程).RP的更多相关文章

  1. setTimeout()和setInterval() 何时被调用执行

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

  2. JS动画三剑客——setTimeout、setInterval、requestAnimationFrame

    一.前言 前端实现动画效果主要有以下几种方法:CSS3中的transition 和 animation ,Javascript 中可以通过定时器 setTimeout.setinterval,HTML ...

  3. 使用定时器settimeout、setInterval执行能传递参数的函数

    无论是window.setTimeout还是window.setInterval,在使用函数名作为调用句柄时都不能带参数,而在许多场合必须要带参数,这就需要想方法解决.经网上查询后整理如下: 例如对于 ...

  4. JS中的执行机制(setTimeout、setInterval、promise、宏任务、微任务)

    1.执行机制 JS 是单线程的,处理 JS 任务(程序)只能一个一个顺序执行,所以 JS 中就把任务分为了同步任务和异步任务.同步的进入主线程先执行,异步的进入Event Table并注册函数,当指定 ...

  5. setTimeout和setInterval从入门到精通

    我们在日常web前端开发中,经常需要用到定时器方法. 前端中的定时器方法是浏览器提供的,并不是ECMAScript规范中的.是window对象的方法. 浏览器中的定时器有两种, 一种是每间隔一定时间执 ...

  6. setTimeout,setInterval原理

    function a() { setTimeout(function(){alert(1)},0); alert(2); } a(); 和其他的编程语言一样,Javascript中的函数调用也是通过堆 ...

  7. 你真的了解setTimeout和setInterval吗?

    博客园的代码排版真难用,编辑时候是好的,一保存就是乱了——本文也同时发表在我另一独立博客上 你真的了解setTimeout和setInterval吗?,可以移步至这里吧 setTimeout和setI ...

  8. JavaScript定时机制setTimeout与setInterval研究

    JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ...

  9. setTimeout,setInterval运行原理

      function a() { setTimeout(function(){alert(1)},0); alert(2); } a(); 和其他的编程语言一样,Javascript中的函数调用也是通 ...

随机推荐

  1. ugui Event.current.mousePosition获取的坐标原点在左上角

    脚本里使用OnGUI(),在鼠标按下时出发EventType.MouseDown事件,此时如果观察Event.current.mousePosition的坐标原点时左上角,即鼠标按下的点越靠近左上角, ...

  2. MySQL实战 | 05 如何设计高性能的索引?

    原文链接:MySQL | 05 如何设计高性能的索引? 上回我们主要研究了为什么使用索引,以及索引的数据结构.今天带你了解如何设计高性能的索引. 其中,有这么一个点,说的是 InnoDB 引擎中使用的 ...

  3. JvisualVm添加远程监控

    一.Weblogic远程监控 1.首先需要在远程的weblogic的域下面,找到/bin/ setDomainEnv.sh ,需要在此文件下加入如下内容: -Dcom.sun.management.j ...

  4. Loadrunner-场景设置以及监控结果分析

    一.Controller的基本工作原理:通过1.2.3设置来模拟用户的操作,收集出4的各种信息 二.场景设置一般步骤 1.新建场景(Controller) 2.添加脚本 3.设置Schedule(设置 ...

  5. java中如何将OutputStream转换为InputStream

    在不需要文件生成的情况下,直接将输出流转换成输入流.可使用下面的三种方法: 如果你曾经使用java IO编程,你会很快碰到这种情况,某个类在OutputStream上创建数据而你需要将它发送给某个需要 ...

  6. vue-cli脚手架build目录中的webpack.dev.conf.js配置文件

    此文章用来解释vue-cli脚手架build目录中的webpack.dev.conf.js配置文件 此配置文件是vue开发环境的wepack相关配置文件 关于注释 当涉及到较复杂的解释我将通过标识的方 ...

  7. 2015.7.24 CAD库中列举五字代码点所属航路及终端区图,左连接的累加

    select decode(fb.tupr,null,'仅航路',decode(fc.aw,null,'仅终端区','航路及终端区')) 范围,pt 五字代码点,fb.tupr 终端区图及程序,fc. ...

  8. mysql索引最左匹配原则的理解

    CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `ci ...

  9. 问题:HttpContext.Current.Session;结果:Session与HttpContext.Current.Session到底有什么区别呢?

    我在做练习的时候遇到了这样一个问题,在母版页页面中写入登录和密码修改的js代码,在登录的方法中写 入 HttpContext.Current.Session.Add("UserPwd&quo ...

  10. Delphi BLE 控件

    TBluetoothLEDevice LDevice.Address;//"00:11:22:DD:EE:FF". LDevice.DeviceName//Mi LDevice.I ...