【问题标题】:WebKit setInterval and system time changeWebKit setInterval 和系统时间变化
【发布时间】:2014-04-11 13:01:14
【问题描述】:

我在创建简单任务时遇到以下问题:使用 WebKit 引擎显示 html 时钟。附加要求是处理系统时间更改,并且它应该在 Windows 上工作。 我已经使用 setInterval 来实现这一点,但是在我向后更改系统时间后它似乎冻结了浏览器。 对我来说,它看起来像 WebKit 问题。通过运行这个简单的代码很容易在 safari 上重现:

<p id="date"></p>
setInterval(SetTime, 1000);
function SetTime() {
document.getElementById('date').textContent=new Date();
}

之后,我使用递归 setTimeout 调用提出了另一种方法。效果一样。

(function loop() {
document.getElementById('date').textContent=new Date();
setTimeout(loop, 1000);
})();

任何想法为什么会发生这种情况以及如何解决这个问题?

【问题讨论】:

    标签: javascript webkit


    【解决方案1】:

    这几乎肯定是 WebKit 的一个问题。

    问题

    当您使用setTimeout 时,您会创建一个具有两个属性的“计时器”:

    • 一个时间戳,设置为现在+指定的延迟
    • 回调在系统时间大于时间戳时触发一次。

    你可以想象一个简单的 setTimeout 实现看起来像这样:

    var timers = [];
    function setTimeout(callback, delay) {
        var id = timers.length;
        timers[id] = {
            callback: callback,
            timestamp: Date.now() + delay
        }
        return id;
    }
    

    这将简单地创建一个计时器并将其添加到列表中。然后,在每个滴答声中,JS 运行时都会检查这些计时器并为那些已触发的计时器执行回调:

    var now = Date.now();
    for(var id in timers) {
        var timer = timers[id];
        if(timer && timer.timestamp < now) {
            callback();
            delete timers[id];
        }
    }
    

    鉴于此实现,想象现在将系统时间(即上述示例中的 Date.now())更改为过去的值 - 计时器的时间戳仍将相对于之前的系统时间(即未来)设置.

    setInterval 也存在同样的问题,它(假设 WebKit 中的代码正常)将使用 setTimeout 实现。

    不幸的是,这意味着任何setTimeoutsetInterval 的调用都会受到影响。

    解决方案

    作为替代方案,您可以使用可爱的window.requestAnimationFrame 方法对每个报价执行回调。我根本没有对此进行测试,但无论系统时间如何,它都应该在每次滴答时继续触发。

    作为奖励,每次它触发回调时,都会将当前时间戳作为参数传递。通过跟踪传递给回调的前一个时间戳,您可以轻松地检测到向后的系统时间变化:

    var lastTimestamp;
    var onNextFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
    
    var checkForPast = function(timestamp) {
        if(lastTimestamp && timestamp < lastTimestamp) {
            console.error('System time moved into the past! I should probably handle this');
        }
        lastTimestamp = timestamp;
        onNextFrame(checkForPast);
    };
    
    onNextFrame(checkForPast);
    

    结论

    这对您来说可能不是好消息,但您可能应该重写整个应用程序以使用 requestAnimationFrame - 它似乎更适合您的需求:

    var onNextFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
    var dateElem = document.getElementById('date');
    
    var updateDate = function(timestamp) {
        dateElem.textContent = new Date();
        onNextFrame(updateDate);
    };
    onNextFrame(updateDate);
    

    【讨论】:

    • 谢谢吉姆。我会尝试你的解决方案。我已经向 WebKit 团队报告了这个问题。
    • 很高兴为您服务。请让我知道这是否有效(如果有效,请接受答案)!
    猜你喜欢
    • 2011-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-16
    相关资源
    最近更新 更多