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);
Danke Jim. Ich werde deine Lösung versuchen. Ich habe dieses Problem bereits dem WebKit-Team gemeldet. – Sleipnir
Ich bin froh, dass ich Ihnen behilflich sein kann. Bitte lassen Sie mich wissen, wenn das funktioniert (und die Antwort akzeptieren, wenn ja)! –