2014-04-11 9 views
8

Ich habe folgendes Problem bei der Erstellung einfacher Aufgabe: HTML-Uhr mit WebKit-Engine angezeigt. Eine zusätzliche Anforderung bestand darin, die Systemzeit zu ändern und dass es unter Windows funktionieren sollte. Ich habe setInterval verwendet, um dies zu erreichen, aber es scheint Browser einzufrieren, nachdem ich die Systemzeit rückwärts ändere. Für mich sieht es wie WebKit Problem aus. Es ist leicht, auf Safari zu reproduzieren, indem Sie diesen einfachen Code ausgeführt wird:WebKit setInterval und Systemzeit ändern

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

Danach habe ich einen anderen Ansatz mit rekursiven setTimeout Anruf gemacht haben. Gleicher Effekt.

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

Irgendwelche Ideen, warum ist das passiert und wie man das umgeht?

Antwort

2

Dies ist fast definitiv ein Problem mit WebKit.

Das Problem

Wenn Sie setTimeout verwenden, können Sie einen 'Timer' mit zwei Eigenschaften erstellen:

  • A Zeitstempel, auf jetzt die angegebene Verzögerung +
  • A Rückruf einmal feuern, wenn die Systemzeit größer als der Zeitstempel ist.

Sie können eine naive Implementierung von setTimeout vorstellen etwas wie folgt aussehen:

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

Dies würde einfach einen Timer erstellen und sie zu einer Liste hinzuzufügen. Dann wird auf jedem Tick, würde die JS Laufzeit dieser Timer überprüfen und die Rückrufe für diejenigen auszuführen, die gefeuert haben:

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

diese Implementierung gegeben, jetzt stellen Sie die Systemzeit zu ändern (dh Date.now() in den Beispielen oben) ein Wert in der Vergangenheit - der Zeitstempel des Timers wird immer noch relativ zur vorherigen Systemzeit gesetzt (dh in der Zukunft).

Das gleiche Problem existiert mit setInterval, die (unter der Annahme, dass sane Code in WebKit) mit setTimeout implementiert werden.

Leider bedeutet dies, dass alle Aufruf von setTimeout oder setInterval leiden wird.

Die Lösung

Als Alternative können Sie die schöne window.requestAnimationFrame Methode verwenden, um einen Rückruf bei jedem Tick auszuführen. Ich habe das gar nicht getestet, aber es sollte weiterhin auf jeden Tick brennen, unabhängig von der Systemzeit.

Als Bonus wird jedes Mal, wenn der Rückruf ausgelöst wird, der aktuelle Zeitstempel als Parameter übergeben.Durch das Verfolgen des vorherigen Zeitstempel auf Ihren Rückruf übergeben, könnte man leicht nach hinten Systemzeit Änderungen erkennen:

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

Schlussfolgerungen

Das ist nicht eine gute Nachricht für Sie sein könnte, aber Sie sollten wahrscheinlich die gesamte Anwendung neu schreiben zu verwenden requestAnimationFrame sowieso - es scheint viel mehr für Ihre Bedürfnisse geeignet:

var onNextFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame; 
var dateElem = document.getElementById('date'); 

var updateDate = function(timestamp) { 
    dateElem.textContent = new Date(); 
    onNextFrame(updateDate); 
}; 
onNextFrame(updateDate); 
+0

Danke Jim. Ich werde deine Lösung versuchen. Ich habe dieses Problem bereits dem WebKit-Team gemeldet. – Sleipnir

+0

Ich bin froh, dass ich Ihnen behilflich sein kann. Bitte lassen Sie mich wissen, wenn das funktioniert (und die Antwort akzeptieren, wenn ja)! –