[英]WebKit setInterval and system time change
I have encountered following issue when creating simple task: displaying html clock by using WebKit engine. 创建简单任务时遇到以下问题:使用WebKit引擎显示html时钟。 Additional requirement were to handle system time change and that it should work on Windows. 附加要求是处理系统时间更改,并且该更改应在Windows上运行。 I have used setInterval to achive this but it seems to freeze browser after I change system time backward. 我已经使用setInterval达到了这个目的,但是在向后更改系统时间后,它似乎冻结了浏览器。 For me it looks like WebKit issue. 对我来说,它看起来像WebKit问题。 It is easy to reproduce on safari by running this simple code: 通过运行以下简单代码,可以轻松地在safari上重现代码:
<p id="date"></p>
setInterval(SetTime, 1000);
function SetTime() {
document.getElementById('date').textContent=new Date();
}
After that I have made another approach with recursive setTimeout call. 之后,我使用了递归setTimeout调用的另一种方法。 Same effect. 效果一样。
(function loop() {
document.getElementById('date').textContent=new Date();
setTimeout(loop, 1000);
})();
Any ideas why is that happening and how to go around this? 有什么想法为什么会发生以及如何解决这个问题?
This is almost definitely an issue with WebKit. WebKit几乎绝对是一个问题。
When you use setTimeout
, you create a 'timer' with two properties: 使用setTimeout
,将创建具有两个属性的“计时器”:
You can imagine a naïve implementation of setTimeout
looking something like this: 您可以想象一个简单的setTimeout
实现看起来像这样:
var timers = [];
function setTimeout(callback, delay) {
var id = timers.length;
timers[id] = {
callback: callback,
timestamp: Date.now() + delay
}
return id;
}
This would simply create a timer and add it to a list. 这将简单地创建一个计时器并将其添加到列表中。 Then, on each tick, the JS runtime would check these timers and execute the callbacks for those that have fired: 然后,在每个刻度上,JS运行时将检查这些计时器并执行已触发的回调:
var now = Date.now();
for(var id in timers) {
var timer = timers[id];
if(timer && timer.timestamp < now) {
callback();
delete timers[id];
}
}
Given this implementation, imagine now changing the system time (ie Date.now()
in the examples above) to a value in the past -- the timer's timestamp will still be set relative to the previous system time (ie in the future). 有了这种实现,想象一下现在将系统时间(即上述示例中的Date.now()
)更改为过去的值-计时器的时间戳仍将相对于先前的系统时间(即将来)进行设置。
The same issue exists with setInterval
, which (assuming sane code in WebKit) will be implemented using setTimeout
. setInterval
存在相同的问题(假设WebKit中使用合理的代码)将使用setTimeout
实现。
Unfortunately, this means that any invocation of setTimeout
or setInterval
is going to suffer. 不幸的是,这意味着setTimeout
或setInterval
任何调用都会受到影响。
As an alternative, you can use the lovely window.requestAnimationFrame
method to perform a callback on each tick. 或者,您可以使用可爱的window.requestAnimationFrame
方法在每个刻度上执行回调。 I haven't tested this at at all, but it should continue to fire on each tick, regardless of the system time. 我根本没有测试过,但是无论系统时间如何,它应该在每个刻度上继续触发。
As a bonus, each time it fires the callback, you get passed the current timestamp as a parameter. 另外,每次触发回调时,您都会将当前时间戳作为参数传递。 By keeping track of the previous timestamp passed to your callback, you could easily detect backwards system time changes: 通过跟踪传递给回调的前一个时间戳,您可以轻松地检测到系统时间的倒退:
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);
This might not be great news for you, but you should probably rewrite your entire application to use requestAnimationFrame
anyway - it seems much more suited to your needs: 对于您来说,这可能不是个好消息,但是您可能应该重写整个应用程序以使用requestAnimationFrame
似乎更适合您的需求:
var onNextFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
var dateElem = document.getElementById('date');
var updateDate = function(timestamp) {
dateElem.textContent = new Date();
onNextFrame(updateDate);
};
onNextFrame(updateDate);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.