SetInterval不同于Chrome和Firefox



以下代码在Firefox和Chrome之间打印不同的结果

var start = Date.now()
var id = setInterval(function interval() {
    var whileStart = Date.now()   
    console.log(whileStart - start)
    while (Date.now() - whileStart < 250) { 
    }
}, 100)
setTimeout(function timeout() {
    clearInterval(id)
    console.log('timeout',Date.now() - start)       
}, 400)

Chrome 74印刷:

100
351
605
timeout 855

Firefox 67印刷:

101
351
timeout 601

为什么?

添加settimeout延迟,结果仍然不同。

var start = Date.now()
var id = setInterval(function interval() {
    var whileStart = Date.now()   
    console.log(whileStart - start)
    while (Date.now() - whileStart < 250) { 
    }
}, 100)
setTimeout(function timeout() {
    clearInterval(id)
    console.log('timeout',Date.now() - start)       
}, 500)

settimeout在指定的延迟后以第一个机会处理您的请求。一旦延迟经过,并且呼叫堆栈为空您的请求将被处理。这意味着根据浏览器的发动机还在进行的情况下,定时可能会有些变化。

这是因为Chrome的setInterval实现确实纠正了每个调用之间的漂移。因此,它不是在间隔回调结束时再次盲目调用setTimeout(fn, 250),而是确实会有setTimeout(fn, max(now - 250, 0))

所以给出

t    Firefox (no drift control)   |   Chrome (drift control)
     ––––––––––––––––––––––––––––––––––––––––––––––––––––––––
0    schedule interval @100ms         schedule interval @100ms
0    schedule timeout  @400ms         schedule timeout  @400ms

100  exec interval callback           exec interval callback
     => block 250ms                   => block 250ms
     ...                              ...
350  schedule interval @450ms         schedule interval @350ms
       (now + 100ms)                    (now + max(100ms - 250ms, 0))
350                                   exec interval callback
                                      => block 250ms
400  exec timeout callback            ...
       => cancel interval             ...
                                      ...
600                                   schedule interval @600ms
                                      exec interval callback
                                      => block 250ms
                                      ...
850                                   schedule interval @850ms
                                      exec timeout callback
                                        => cancel interval

请注意,最后一个间隔@600实际上取决于首先计划的timeoutinterval的哪个。

,还要注意,Chrome的行为可能会在不久的将来成为标准:https://github.com/whatwg/html/issues/3151

由于两个浏览器之间的代码差异而产生的差异。

  1. 如果您扭转了启动间隔和超时计时器的顺序,则Chrome的行为与Firefox相同:

"use strict";
var start = Date.now()
setTimeout(function timeout() { // start the timeout first
    clearInterval(id)
    console.log('timeout',Date.now() - start)       
}, 400)
var id = setInterval(function interval() {
    var whileStart = Date.now()   
    console.log(whileStart - start)
    while (Date.now() - whileStart < 250) { 
    }
}, 100)

  1. 如果您保留与帖子中相同的订单,但将阻塞减少到50毫秒,则间隔时间在Chrome中的400ms标记处被调用,并在阻止事件循环时阻止了暂停。但是在Firefox中,首先调用400ms的Settimeout,清除了间隔计时器,并防止以400ms标记为400ms的间隔计时器。

简而言之,间隔和计时器的超时处理在两个浏览器中的编码不同,在上面的情况下,Firefox似乎在间隔调用调用之间的间隔中增加了10ms(如HTML 5.2的7.5节所示(。

最新更新