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

http://www.cnblogs.com/yangjunhua/archive/2012/04/12/2444106.html

下面的参考:http://evantre.iteye.com/blog/1718777

1、选题缘起

在知乎上瞎逛的时候看到一个自问自答的问题:

知乎上,前端开发领域有哪些值得推荐的问答?,然后在有哪些经典的 Web 前端或者 JavaScript 面试笔试题?中看到这么一题:以下两段代码有什么不同?

(1)

  1. setTimeout( function() {
  2. /* 代码块... */
  3. setTimeout( arguments.callee, 10 );
  4. }, 10 );

(2)

  1. setInterval( function() {
  2. /*代码块... */
  3. }, 10 );

2、长话短说

对于这个问题本身而言,并不需要花费太多篇幅和时间来说明。简单地说setTimeout()指在指定时间后执行一次指定函数,setInterval()指每隔一段所指定的时间执行一次指定函数,两个方法都称为定时器,不是ECMAScript的内容,而是属于BOM。

0 --x--10,分成3个区间

下面先列出关于这个问题的几个结论:

(1)当其中定时器指定函数所需执行时间几乎为0ms时,setTimeout()链式调用和setInterval()可以说没有什么明显区别,均每间隔10ms开始执行一次代码块中的内容,每次所需时间0ms。(这种情况应该少见,或者是一种理想假定)

(2)当其中定时器指定函数执行所需时间0 < T < 10ms时,二者差别非常明显。假定执行队列中没有其他任务,定时器所指定函数可以在指定间隔时间添加到执行队列并立即执行,假定定时器所指定函数执行时间需要5ms,0ms时开始执行定时器。

那么对于setTimeout()链式调用而言,在10ms,setTimeout()所指定函数被添加到执行队列并立即执行,到15ms,代码块执行完毕,此时又创建一个定时器,那么间隔10ms即在25ms时,函数第二次执行。

而对于setInterval()而言,同样,在10ms时,setTInterval()所指定函数开始执行,在15ms时候,代码块也就是所指定函数执行完毕,然而下一次开始执行函数的时间将是20ms时,是从第一次开始执行的时间算起间隔10ms,而与函数执行所需时间无关(当然这里时间不能大于10ms,前面已经作了假设)。

(3)当其中定时器指定函数执行所需时间 T > 10ms时,二者差别同样非常明显。假定执行队列中没有其他任务,定时器所指定函数可以在指定间隔时间添加到执行队列并立即执行,假定定时器所指定函数执行时间需要20ms,0ms时开始执行定时器。

那么对于setTimeout()链式调用而言,在10ms时,setTimeout()所指定函数被添加到执行队列并立即执行,到30ms,代码块执行完毕,此时又创建一个定时器,那么间隔10ms即在40ms时,函数第二次执行。

而对于setInterval()而言,同样,在10ms时,setInterval()所指定函数开始执行,在30ms时候,代码块也就是所指定函数执行完毕,原本按照指定下一次开始执行函数的时间应该是20ms时,但是这里函数的执行时间已经超过了20ms,那么按指定,下一次执行该函数的时间应该是30ms时,而在此时函数第一次执行刚好完毕,那么就会马上执行第二次,结果就是在第一次执行完毕,到下一次开始执行这个期间几乎没有时间间隔。

这也就是setTimeout()链式调用和setInterval()的区别之处,两种用法本来所表达的就不是一个意思。setTimeout()链式调用所指定的时间间隔是指函数前一次执行结束到后一次执行开始之间的间隔,而setInterval()所指定的时间是指函数前一次开始执行的时间和后一次开始执行的时间之间的间隔。

(4)那么在实际应用中,应该根据你想表达的意思选择合适的方法,比如如果你是想从某个时刻开始,每隔一分钟响铃一次用来提示时间,该任务每次执行时间时间需要50ms,这个时候你就应该使用setInterval(),时间间隔指定为1分钟(自己换成毫秒)。如果这种情况你用setTimeout()链式调用指定相同的时间,那么结果会是第一次响铃时间恰好是0秒的时候,但第二次响铃的时候却是0秒加50ms(因为你是在响铃任务完成后立即添加的又一个setTimeout()定时器),然后结果越来越脱离你的预期。因此我觉得setInterval()更像是一个闹钟,而setTimeout()则是真正的定时器。

(5)按照Nicholas的说法是,一般认为,使用超时调用模拟间歇调用是一种最佳模式,开发环境下,很少使用真正的间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束前启动,但仅当没有改定时器的任何其他代码事例时,才将定时器代码添加到队列中,结果就可能会跳过一些定时器代码。

3、实践分析

(该段可略过)刚看到这个题目的时候感觉有那么点印象,但是再回忆时却发现记忆并不深刻,这也源于我平时看书不太认真之缘故,因为关于这个问题在Nicholas的《JavaScript高级程序设计(第三版)》一书中是有比较明确的说明的。当时看到这个问题时也是想当然的就Google或者Baidu,确实也检索到很多有价值的信息,比如有的结果就将我引回到Nicholas的书中。相信很多人对这个问题都已经很熟悉,但也可能会有很多新手还会不太明白,于是我打算站在前人的基础对关于setTimeout()和setInterval()的问题进行一下搜集、整理和归纳,也谈谈自己的一些想法。同时在鬼懿IT-成长群:181368696 中谈论这个问题的时候,管理员安也让我将该问题整理成文,于是又有了不少动力。而在一步步实践验证的过程中发现原本二者的差异是非常明显的,这原本就是一个很基础很简单的问题,但既然也费了些时间,就把这个过程记录下来。

1、分析(实验+理论)

setTimeout()和setInterval()都是用来创建定时器,可以实现很多功能。通常情况下,setTimeout()的链式调用和setInterval()都可以让程序在一定的时间间隔内重复执行一段代码。JavaScript是运行于单线程的环境中,只有当进程空闲时才会执行所添加到执行队列中的下一段代码,没有任何代码是可以确保立即执行的。不管是setTimeout()还是setInterval()所指定的时间都只能确保在何时将定时器指定的代码添加到执行队列中,而不是何时实际执行代码,且setTimeout()和setInterval()指定的定时器代码只有在创建它的函数执行完成之后,才有可能被执行。还有一个问题就是定时器所指定的代码所执行的时间可能会大于定时器指定的时间间隔。下面以具体的事例来说明各种情况。

(1)为了进行测试,写了一个延时函数,大致延时139ms,具体如下

  1. function delay() {
  2. //此处添加时间是为了测试这个函数会延时多久
  3. var start = Date.now(),
  4. end;
  5. for (var i = 0; i < 10000; i++) {
  6. for (var j = 0; j < 10000; j++);
  7. }
  8. end = Date.now();
  9. console.log( end - start );
  10. }

通过下面这段代码进行测试

  1. for (var i = 0; i < 200; ++i) {
  2. delay();
  3. }

(这段代码执行时候会阻塞页面,仅仅用于测试)运行经过多次测试取一段稳定值,延时时间大致平均为139ms(根据具体环境,我是在Chrome22/Win7下运行的)

(2)接下来分别使用setTimeout()链式调用和setInterval()分别来调用这段延时代码,第二个参数均设置为50ms。

首先使用setTimeout()链式调用,代码如下

  1. function delay() {
  2. for (var i = 0; i < 10000; i++) {
  3. for (var j = 0; j < 10000; j++);
  4. }
  5. }
  6. var before_time = Date.now(),
  7. after_time,
  8. end;
  9. setTimeout( function() {
  10. after_time = Date.now();
  11. var start = after_time;
  12. console.log( after_time - before_time );//前一次开始执行到后一次开始执行时间间隔
  13. console.log( start - end ); //前一次结束到后一次开始之间时间间隔
  14. before_time = after_time;
  15. delay();
  16. end = Date.now();
  17. setTimeout( arguments.callee, 50 );
  18. }, 50 );

运行结果如下:

像我们预期的那样,前一次结束到后一次开始之间间隔50ms运行。前一次开始执行到后一次开始执行时间间隔约210ms,基本稍微大于每次的139ms加上间隔的50ms,其中有一些其他开销。

现在使用setInterval()来看一看,代码如下

  1. function delay() {
  2. for (var i = 0; i < 10000; i++) {
  3. for (var j = 0; j < 10000; j++);
  4. }
  5. }
  6. var before_time = Date.now(),
  7. after_time,
  8. end;
  9. setInterval( function() {
  10. after_time = Date.now();
  11. var start = after_time;
  12. console.log( after_time - before_time );//前一次开始执行到后一次开始执行时间间隔
  13. console.log( start - end ); //前一次结束到后一次开始之间时间间隔
  14. before_time = after_time;
  15. delay();
  16. end = Date.now();
  17. }, 50 );

运行结果如下

很明显,前一次结束到后一次开始之间几乎没有时间间隔。由于没有间隔,前一次开始执行到后一次开始执行时间间隔140ms,基本等于delay()函数的每次139ms。

(3)接下来分别使用setTimeout()链式调用和setInterval()分别来调用这段延时代码,与上面不同的是第二个参数均设置为200ms。

首先来看看setTimeout()链式调用,代码依然和上面一样,只是时间改为200ms,结果如下

结果不出所料,前一次结束到后一次开始之间间隔200ms运行。前一次开始执行到后一次开始执行时间间隔约355ms,稍微大于200ms+139ms。

下面再来看看setInterval(),结果如下

可以看到前一次开始执行到后一次开始执行时间间隔恰好为指定的200ms,而前一次结束到后一次开始之间间隔稍微小于200ms - 139ms。

参考:

【1】Nicholas,JavaScript高级程序设计(第三版),人民邮电出版社,2012

【2】Nicholas,高性能JavaScript,电子工业出版社,2010

关于JavaScript中的setTimeout()链式调用和setInterval()探索的更多相关文章

  1. JavaScript设计模式——方法的链式调用

    方法的链式调用: (function() { //私有类 function _$ (els) { this.elements = []; for(var i = 0, len = els.length ...

  2. JavaScript设计模式 Item 5 --链式调用

    1.什么是链式调用 这个很容易理解,例如: $(this).setStyle('color', 'red').show(); 一般的函数调用和链式调用的区别:调用完方法后,return this返回当 ...

  3. 在Vue单页面应用中使用Promise链式调用

    eg: this.commonLoginFun().then((res) => { if (res.errNo === 0) { const { isLogin } = res.data; if ...

  4. JavaScript实现链式调用

    学习Jquery的时候,我们通常会看到链式调用的写法 $(window).addEvent('load', function(){ $('test').show().setStyle('color', ...

  5. js实现方法的链式调用

    假如这里有三个方法:person.unmerried();person.process();person.married();在jQuery中通常的写法是:person.unmerried().pro ...

  6. JavaScript中的链式调用

    链模式 链模式是一种链式调用的方式,准确来说不属于通常定义的设计模式范畴,但链式调用是一种非常有用的代码构建技巧. 描述 链式调用在JavaScript语言中很常见,如jQuery.Promise等, ...

  7. javascript学习(10)——[知识储备]链式调用

    上次我们简单的说了下单例的用法,这个也是在我们java中比较常见的设计模式. 今天简单说下链式调用,可能有很多人并没有听过链式调用,但是其实只要我简单的说下的话,你肯定基本上都在用,大家熟知的jQue ...

  8. JavaScript链式调用

    1.什么是链式调用? 这个很容易理解,例如 $('text').setStyle('color', 'red').show(); 一般的函数调用和链式调用的区别:链式调用完方法后,return thi ...

  9. 编程中的链式调用:Scala示例

    编程中的链式调用与Linux Shell 中的管道类似.Linux Shell 中的管道 ,会将管道连接的上一个程序的结果, 传递给管道连接的下一个程序作为参数进行处理,依次串联起N个实用程序形成流水 ...

随机推荐

  1. const define 定义常量的区别

    1.用const定义常量在编译的时候,提供了类型安全检查,而define 只是简单地进行字符串的替换 2.const定义的常量,会分配相应的内存空间.而define没有分配空间,只是在程序中与处理的时 ...

  2. Windows Phone 为指定容器内的元素设置样式

    在Windows Phone中设置元素样式有多种 拿TextBlock来说 1.我们可以直接在控件上设置: <TextBlock Text="自身样式设置" Width=&q ...

  3. 《SELinux安全上下文的管理(含图)》RedHat6.3——步骤详细、条理清晰

    1.为什么浏览器只识别/var/www/html下的文件? 2.为什么不识别别的目录下的index.html文件呢? 3.这里牵扯到身份证,先安装软件包. 4.打开selinux 5.建立一个新的目录 ...

  4. 利用cglib生成动态java bean

    cglib详细学习 http://blog.csdn.net/u010150082/article/details/10901641 cglib-nodep jar报下载 http://grepcod ...

  5. Cassandra1.2文档学习(18)—— CQL数据模型(下)

    三.集合列 CQL 3 引入了一下集合类型: •set •list •map 在关系型数据库中,允许用户拥有多个email地址,你可以创建一个email_addresses表与users表存在一个多对 ...

  6. 有关linux日志分析的详细介绍

    linux的日志文件可以帮助我们了解系统所处的状态,比如查出哪些用户有登入,及其它安全相关的一些问题. linux下的日志分析. 以下内容,部分参考了:探讨 linux 日志分析 这篇文章. 1.了解 ...

  7. php Zend Opcache,xcache,eAccelerator缓存优化详解及对比

    XCACHE XCache 是一个开源的 opcode 缓存器/优化器, 这意味着他能够提高您服务器上的 PHP 性能. 他通过把编译 PHP 后的数据缓冲到共享内存从而避免重复的编译过程, 能够直接 ...

  8. unity3d应用内分享(微信、微博等)的实现

    问题:如何在unity3d的游戏中实现分享功能,如图 思路: 1.分享功能的实现方式有多种,较方便快捷的一种是直接调用android的API来调出系统的分享界面 2.unity3d里面调用androi ...

  9. epoll_create, epoll_ctl和epoll_wait

    参考代码 #include <iostream> #include <sys/socket.h> #include <sys/epoll.h> #include & ...

  10. C# send mail with outlook and word mailmerge

    http://msdn.microsoft.com/en-us/library/microsoft.office.interop.word.document_members(v=office.15). ...