I have encountered following issue when creating simple task: displaying html clock by using WebKit engine. Additional requirement were to handle system time change and that it should work on Windows. I have used setInterval to achive this but it seems to freeze browser after I change system time backward. For me it looks like WebKit issue. It is easy to reproduce on safari by running this simple code:
<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. 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.
When you use setTimeout
, you create a 'timer' with two properties:
You can imagine a naïve implementation of setTimeout
looking something like this:
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:
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).
The same issue exists with setInterval
, which (assuming sane code in WebKit) will be implemented using setTimeout
.
Unfortunately, this means that any invocation of setTimeout
or setInterval
is going to suffer.
As an alternative, you can use the lovely window.requestAnimationFrame
method to perform a callback on each tick. 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:
var onNextFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
var dateElem = document.getElementById('date');
var updateDate = function(timestamp) {
dateElem.textContent = new Date();
onNextFrame(updateDate);
};
onNextFrame(updateDate);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.