WebKit setInterval and changing system time

I encountered the following problem when creating a simple task: displaying html-hours using the WebKit mechanism. Additional requirements were to handle the change in system time and that it should work on Windows. I used setInterval to achieve this, but it seems to freeze the browser after changing the system time back. For me, this seems like a WebKit problem. It's easy to play safari by executing this simple code:

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

After that, I made another approach with a recursive call to setTimeout. The same effect.

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

Any ideas why this is happening and how to get around this?

+8
javascript webkit
source share
1 answer

This is almost certainly a problem with WebKit.

Problem

When you use setTimeout , you create a "timer" with two properties:

  • Current timestamp + preset delay
  • Callback for fire once when the system time is more than a timestamp.

You can imagine a naive setTimeout implementation that looks something like this:

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

This will simply create a timer and add it to the list. Then, at each tick, JS runtime checks these timers and makes callbacks for running ones:

 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 that now change the system time (i.e. Date.now() in the above examples) to a value in the past - the timer timestamp will still be set relative to the previous system time (i.e., in future).

The same problem exists with setInterval , which (provided that the correct code in WebKit) is implemented using setTimeout .

Unfortunately, this means that any call to setTimeout or setInterval will suffer.

Decision

Alternatively, you can use the beautiful window.requestAnimationFrame method to call back each tick. I did not test this at all, but it should continue to shoot at every tick, regardless of the system time.

As a bonus, every time it launches a callback, you pass the current timestamp as a parameter. By tracking the previous timestamp passed to your callback, you can easily detect backward changes in system time:

 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); 

Conclusion

This may not be big news for you, but you should probably rewrite your entire application to use requestAnimationFrame anyway - it seems a lot more suitable for 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); 
+2
source share

All Articles