V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
hansnow
V2EX  ›  问与答

JavaScript 中 setTimeout 会产生双倍时间间隔

  •  
  •   hansnow · 2016-08-24 10:04:27 +08:00 · 3199 次点击
    这是一个创建于 3018 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下

    var testDelay = function testDelay (interval) {
        setTimeout(function () {
            console.log(new Date().getSeconds())
            setTimeout(arguments.callee, interval)
        }, interval)
    }
    
    testDelay(1000)
    

    控制台输出如下

    控制台输出

    预想的效果是每隔 1s 进行一次输出,但是实际效果却是时间间隔变成了 2s ,请问这是为什么呢?

    第 1 条附言  ·  2016-08-24 14:31:25 +08:00

    从楼下各位的回复来看,setTimeout的行为确实有些随机性的。《JavaScript高级程序设计(第三版)》的第22章里面说,“关于定时器要记住的最重要的事情是,指定的时间间隔表示何时将定时器的代码添加到队列,而不是合适实际执行代码”。

    一个比较实际的例子是,用这个代码实现toast效果时,当执行下面这个语句时

    setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
    

    由于setTimeout的不确定性,在很多移动端浏览器上可能会出现fadeOut动画的结尾出现闪烁的现象。

    23 条回复    2016-08-24 17:03:24 +08:00
    newghost
        1
    newghost  
       2016-08-24 10:11:01 +08:00
    为毛我这里是一秒一个?

    var testDelay = function testDelay (interval) {
    setTimeout(function () {
    console.log(new Date().getSeconds())
    setTimeout(arguments.callee, interval)
    }, interval)
    }

    testDelay(1000)
    undefined
    VM635:3 36
    VM635:3 37
    VM635:3 38
    VM635:3 39
    VM635:3 40
    VM635:3 41
    VM635:3 42
    VM635:3 43
    shiye515
        2
    shiye515  
       2016-08-24 10:13:43 +08:00
    原因就是你电脑太卡了,执行 console.log 也要+1s
    hansnow
        3
    hansnow  
    OP
       2016-08-24 10:15:03 +08:00
    @newghost what????这我就更想不明白了啊。。。
    Chrome 版本: 51.0.2704.84 m (64-bit)
    Windows 版本: Microsoft Windows [版本 10.0.10586]
    FrankFang128
        4
    FrankFang128  
       2016-08-24 10:16:54 +08:00
    你没懂 setTimeout
    它不保证准时的
    hansnow
        5
    hansnow  
    OP
       2016-08-24 10:18:20 +08:00
    @shiye515 应该不是这样吧?下面的代码就可以输出两个相邻的数字

    ```javascript
    var testDelay = function testDelay (interval) {
    console.log(new Date().getSeconds())
    setTimeout(function () {
    console.log(new Date().getSeconds())
    }, interval)
    }

    testDelay(1000)
    ```
    hansnow
        6
    hansnow  
    OP
       2016-08-24 10:20:36 +08:00
    @FrankFang128 这个我稍微知道一点, setTimeout 只是把代码加入了执行队列。但是前面只有一个 console.log 额。。。打个 log 出来应该不会消耗 1s 的时间吧,而且你看我在#5 贴的代码,是可以输出两个连续的数字的,说明 console.log 没消耗 1s 的时间
    UnisandK
        7
    UnisandK  
       2016-08-24 10:22:03 +08:00
    zzNucker
        8
    zzNucker  
       2016-08-24 10:25:38 +08:00
    重新启动浏览器。
    笔记本请插上电源用高性能模式。。

    虽然我觉得省电模式也不可能 2000ms 一个 tick
    lwbjing
        9
    lwbjing  
       2016-08-24 10:26:24 +08:00
    var testDelay = function testDelay (interval) {
    setTimeout(function () {
    console.log(new Date().getSeconds())
    setTimeout(arguments.callee, interval)
    }, interval)
    }

    testDelay(1000)
    undefined
    VM139:3 53
    VM139:3 54
    VM139:3 55
    VM139:3 56
    VM139:3 57
    VM139:3 58
    VM139:3 59
    VM139:3 0
    VM139:3 1
    VM139:3 2
    hansnow
        10
    hansnow  
    OP
       2016-08-24 10:28:18 +08:00
    @newghost
    @FrankFang128
    @UnisandK
    @zzNucker

    我已经凌乱了,把 interval 改成 1s 以上的话是正常的,然后我又试着改到了 100ms ,结果就是下面这样的输出。。。这也太看心情了吧。。。

    exoticknight
        11
    exoticknight  
       2016-08-24 10:36:38 +08:00
    在 MDN 上查到 “ Timeouts in inactive tabs clamped to >=1000ms ”
    然后我也看到本身是 1s 一次的输出的确会变成 2s
    +1s
    xxxyyy
        12
    xxxyyy  
       2016-08-24 10:37:50 +08:00 via Android
    你把毫秒也打印出来一看就清楚了,比如上一次是 1.999 ,那下一次有可能是 3.100 ,毕竟这个并不是很准的。
    mdluo
        13
    mdluo  
       2016-08-24 10:39:21 +08:00
    没有仔细看代码,但是感觉这个应该是 Chrome 调试工具的 Bug ,我之前也遇到过类似的问题,打断点输出和不打断点输出的结果不一致,升级到 Chrome 52 就好了(我在 Chrome 52 里跑了一下是正常的 1 秒输出一次)
    mdluo
        14
    mdluo  
       2016-08-24 10:46:39 +08:00
    至于 @FrankFang128 说的不准时, setTimeout 确实会不准时,但是不至于误差到秒级别。这个应该跟代码的执行顺序有关系,看调试工具怎么判断 console.log 的输出和执行 setTimeout 回调之间的先后顺序

    没仔细看代码,欢迎打脸
    subpo
        15
    subpo  
       2016-08-24 10:52:02 +08:00
    少掉的一秒到哪里去了呢?
    exoticknight
        16
    exoticknight  
       2016-08-24 10:54:54 +08:00
    @subpo
    通常来说,是被前面的代码执行挤掉了
    或者说是被……
    66beta
        17
    66beta  
       2016-08-24 10:56:00 +08:00
    setTimeout 不精准,但这个个案来看,就是楼主机器太卡 XD
    dacapoday
        18
    dacapoday  
       2016-08-24 10:56:26 +08:00
    这哪是 bug,你也知道是添加到队列而不是中断触发事件,浏览器除了 JS 就不干其他活了?这种情况应该用 requestAnimationFrame
    shenxian
        19
    shenxian  
       2016-08-24 11:00:11 +08:00
    +1s
    FrankFang128
        20
    FrankFang128  
       2016-08-24 12:59:01 +08:00   ❤️ 1
    自己看 MDN
    浏览器执行 setTimeout 是看心情的。
    xujif
        21
    xujif  
       2016-08-24 14:47:30 +08:00
    是 cpu 降频节能了 chrome 没反应过来?
    lshero
        22
    lshero  
       2016-08-24 15:15:56 +08:00
    setTimeout 也要按基本法
    ksco
        23
    ksco  
       2016-08-24 17:03:24 +08:00
    因为某种神秘力量自动给你续了一秒。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1343 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:45 · PVG 01:45 · LAX 09:45 · JFK 12:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.