Chrome: Zeitüberschreitungen / Intervalle in Hintergrundregistern ausgesetzt?

130

Ich testete die Genauigkeit der setTimeoutmit diesem Test . Jetzt habe ich festgestellt, dass (wie erwartet) setTimeoutnicht sehr genau ist, aber für die meisten Geräte nicht dramatisch ungenau. Wenn ich den Test jetzt in Chrome ausführe und ihn in einer Hintergrundregisterkarte laufen lasse (also zu einer anderen Registerkarte wechseln und dort navigieren), zum Test zurückkehre und die Ergebnisse überprüfe (wenn der Test beendet ist), werden sie dramatisch geändert. Es sieht so aus, als ob die Zeitüberschreitungen viel langsamer verlaufen sind. In FF4 oder IE9 getestet, ist dies nicht aufgetreten.

Es sieht also so aus, als würde Chrome die Ausführung von Javascript in einem Tab ohne Fokus anhalten oder zumindest verlangsamen. Konnte nicht viel im Netz zu diesem Thema finden. Dies würde bedeuten, dass wir keine Hintergrundaufgaben ausführen können, wie zum Beispiel regelmäßige Überprüfungen auf einem Server mithilfe von XHR-Aufrufen und setInterval(ich vermute, dass das gleiche Verhalten für angezeigt setIntervalwird, einen Test schreiben, wenn die Zeit für mich gekommen ist).

Hat jemand dies angetroffen? Gibt es eine Problemumgehung für diese Unterbrechung / Verlangsamung? Würden Sie es einen Fehler nennen und sollte ich es als solchen einreichen?

KooiInc
quelle
Interessant! Können Sie feststellen, ob Chrome den Timer pausiert und wieder aufnimmt oder neu startet, sobald Sie erneut auf die Registerkarte zugreifen? Oder ist das Verhalten zufällig? Könnte es etwas damit zu tun haben, dass Chrome Tabs in unabhängigen Prozessen ausführt?
HyderA
@gAMBOOKa: Schauen Sie sich die Antwort von @ pimvdb an. Es ist wahrscheinlich eine Verlangsamung auf maximal einmal pro Sekunde.
KooiInc
4 Jahre später und dieses Problem besteht immer noch. Ich habe ein setTimeOut für Divs mit einem transition, so dass nicht alle Divs gleichzeitig übergehen, sondern tatsächlich 15 ms nacheinander, wodurch ein rollender Effekt entsteht. Wenn ich zu einem anderen Tab gehe und nach einer Weile zurückkomme, wechseln alle Divs gleichzeitig und das setTimeOutwird vollständig ignoriert. Es ist kein großes Problem für mein Projekt, aber es ist eine seltsame und unerwünschte Ergänzung.
Rvervuurt
Für unsere Animation, die setTimeout in einer Sequenz aufrief, bestand die Lösung für uns nur darin, sicherzustellen, dass wir uns an das Handle / die ID des Timers erinnern (der von setTimeout zurückgegeben wird), und bevor wir einen neuen Timer einstellen, rufen wir zuerst clearTimeout auf, wenn wir dies getan haben habe den Griff bekommen. In unserem Fall bedeutet dies, dass bei der Rückkehr zur Registerkarte möglicherweise eine gewisse Seltsamkeit in Bezug auf die Wiedergabe der Animation auftritt, diese sich jedoch recht schnell von selbst löst und die reguläre Animation fortgesetzt wird. Wir hatten anfangs gedacht, dass dies ein Problem ohne Code ist.
Aktion Dan

Antworten:

88

Ich habe kürzlich danach gefragt und es ist Verhalten von Natur aus. Wenn eine Registerkarte inaktiv ist, wird die Funktion nur maximal einmal pro Sekunde aufgerufen. Hier ist die Codeänderung .

Vielleicht hilft dies: Wie kann ich festlegen, dass setInterval auch funktioniert, wenn ein Tab in Chrome inaktiv ist?

TL; DR: Verwenden Sie Web Worker .

pimvdb
quelle
3
danke, ich hätte mit 'inaktiver Registerkarte' suchen sollen. Nicht Englisch als Muttersprache zu sprechen, ist manchmal ein Handicap.
KooiInc
1
@Kooilnc: Kein Problem :) Ich bin auch kein englischer Muttersprachler.
Pimvdb
22

Es gibt eine Lösung für die Verwendung von Web Workern, da diese in einem separaten Prozess ausgeführt werden und nicht verlangsamt werden

Ich habe ein winziges Skript geschrieben, das ohne Änderungen an Ihrem Code verwendet werden kann - es überschreibt einfach die Funktionen setTimeout, clearTimeout, setInterval, clearInterval

Fügen Sie es einfach vor Ihrem gesamten Code ein

http://github.com/turuslan/HackTimer

Ruslan Tushov
quelle
7
Das ist schön und alles andere als zu beachten: 1. Arbeiter haben keinen Zugriff auf das DOM, 2. Arbeiter werden immer nur ausgeführt, wenn sie sich in einer eigenen Datei befinden. In vielen Fällen ist es kein Ersatz für setTimeout.
Madaras Geist
1
Sie haben Recht, aber einige moderne Browser erlauben die Verwendung von Arbeitern ohne eigene Dateien mithilfe von Blobs ( html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers )
Ruslan Tushov
1
Trotzdem fehlen Web Workern viele Funktionen (nämlich DOM), die es ihnen ermöglichen, setTimeout und co sicher zu ersetzen.
Madara Ghost
Was ist mit Code, der im Front-End ausgeführt werden muss, zum Beispiel schwere Grafikverarbeitungsaufgaben, die wir gerne erledigen würden, während wir andere Dinge erledigen?
Michael
Nun, Sie können Worker und Servicemitarbeiter erstellen und die Canvas-API mithilfe einer Daten-URL verwenden. new Worker('data:text/javascript,(' + function myWorkerCode () { /*...*/ } + '()'). Auf diese Weise können Sie auch überprüfen, ob Sie Unterstützung für Importausdrücke haben:try { eval('import("data:text/javascript,void 0")') } catch (e) { /* no support! */ }
Fábio Santos
9

Das Abspielen eines ~ leeren Sounds zwingt den Browser, die Leistung beizubehalten. Ich habe ihn nach dem Lesen dieses Kommentars entdeckt: Wie kann JavaScript in Chrome mit normaler Geschwindigkeit ausgeführt werden, auch wenn der Tab nicht aktiv ist?

Ich benötige eine unbegrenzte Leistung bei Bedarf für ein Browsergame, das WebSockets verwendet. Daher weiß ich aus Erfahrung, dass die Verwendung von WebSockets keine unbegrenzte Leistung gewährleistet, aber aus Tests scheint das Abspielen einer Audiodatei dies zu gewährleisten

Hier sind 2 leere Audio-Loops, die ich zu diesem Zweck erstellt habe. Sie können sie kommerziell frei verwenden: http://adventure.land/sounds/loops/empty_loop_for_js_performance.ogg http://adventure.land/sounds/loops/empty_loop_for_js_performance.wav

(Sie enthalten -58db Rauschen, -60db funktioniert nicht)

Ich spiele sie auf Benutzerwunsch mit Howler.js: https://github.com/goldfire/howler.js

function performance_trick()
{
    if(sounds.empty) return sounds.empty.play();
    sounds.empty = new Howl({
        src: ['/sounds/loops/empty_loop_for_js_performance.ogg','/sounds/loops/empty_loop_for_js_performance.wav'],
        volume:0.5,
        autoplay: true, loop: true,
    });
}

Es ist traurig, dass es keine integrierte Methode gibt, mit der die volle Javascript-Leistung standardmäßig ein- und ausgeschaltet werden kann. Crypto Miner können jedoch alle Ihre Computer-Threads mithilfe von Web Workers ohne Aufforderung entführen: |

Kaan Soral
quelle
Vielen Dank, 58db ist mit Kopfhörern sehr gut zu hören, aber das Stummschalten der Website löst dieses Problem
Kaan Soral
1

Ich habe ein Worker-Intervall- npm-Paket veröffentlicht, das die Implementierung von Interval und ClearInterval mit Web-Workern festlegt, um auf inaktiven Registerkarten für Chrome, Firefox und IE auf dem Laufenden zu bleiben.

Die meisten modernen Browser (Chrome, Firefox und IE) und Intervalle (Fenster-Timer) werden so geklemmt, dass sie in inaktiven Registerkarten nicht öfter als einmal pro Sekunde ausgelöst werden.

Weitere Informationen finden Sie unter

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Timeouts_and_intervals

gorkemcnr
quelle
0

Ich habe meinen jQuery-Kern auf 1.9.1 aktualisiert und die Intervalldiskrepanz in inaktiven Registerkarten behoben. Ich würde das zuerst versuchen und dann andere Optionen zum Überschreiben von Code untersuchen.

Carey Estes
quelle
Von welcher Version haben Sie ein Upgrade durchgeführt? Ich hatte einige Timeout-Probleme (Galerie-Schieberegler) mit Version ~ 1.6
dmi3y
0

Hier ist meine Lösung, die die aktuelle Millisekunde abruft und sie mit der Millisekunde vergleicht, in der die Funktion erstellt wurde. Für das Intervall wird die Millisekunde aktualisiert, wenn die Funktion ausgeführt wird. Sie können das Intervall / Timeout auch anhand einer ID abrufen.

<script>

var nowMillisTimeout = [];
var timeout = [];
var nowMillisInterval = [];
var interval = [];

function getCurrentMillis(){
    var d = new Date();
    var now = d.getHours()+""+d.getMinutes()+""+d.getSeconds()+""+d.getMilliseconds();
    return now;
}

function setAccurateTimeout(callbackfunction, millis, id=0){
    nowMillisTimeout[id] = getCurrentMillis();
    timeout[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisTimeout[id] + +millis)){callbackfunction.call(); clearInterval(timeout[id]);} }, 10);
}

function setAccurateInterval(callbackfunction, millis, id=0){
    nowMillisInterval[id] = getCurrentMillis();
    interval[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisInterval[id] + +millis)){callbackfunction.call(); nowMillisInterval[id] = getCurrentMillis();} }, 10);
}

//usage
setAccurateTimeout(function(){ console.log('test timeout'); }, 1000, 1);

setAccurateInterval(function(){ console.log('test interval'); }, 1000, 1);

</script>
SwiftNinjaPro
quelle