Warum funktioniert dieser setTimeout-basierte Code in Firefox nicht mit einem kleinen Timeout (funktioniert in Internet Explorer / Chrome)?

8

Ich habe den folgenden Code, der den Unterschied beim Aufruf einer lang laufenden Funktion direkt von einem Ereignisauslöser im Gegensatz zu setTimeout() zeigt.

Vorgesehenes Verhalten:

  • Wenn die erste Taste gedrückt wird, erscheint sie gedrückt, die Berechnung läuft für einige Sekunden, dann, wenn die Berechnung beendet ist, erscheint die Taste wieder gedrückt und die zweite Spalte wechselt von "noch nicht berechnen" zu "Berechnung beendet" ". (Ich werde nicht näher erläutern, warum dies geschehen soll; es wird in einer verknüpften Antwort erklärt.)

  • Wenn die zweite Taste gedrückt wird, drückt die Taste sofort; Die zweite Spalte wechselt sofort zum Text "Berechnung ...". Wenn die Berechnung einige Sekunden später abgeschlossen ist, ändert sich die zweite Spalte von "Berechnung ..." in "Berechnung erfolgt".

Was tatsächlich passiert:

  • Dies funktioniert perfekt in Chrome (beide Tasten verhalten sich wie erwartet)

  • Dies funktioniert perfekt in Internet Explorer 8

  • Dies funktioniert NICHT in Firefox (v.25) wie es ist. Insbesondere verhält sich die zweite Schaltfläche 100% als die erste Schaltfläche.

    • Das Ändern des Timeouts in setTimeout() von 0 auf 1 hat keine Auswirkungen

    • Ändern der Zeitüberschreitung in setTimeout() von 0 in 500 funktioniert

Was mich mit einem großen Rätsel konfrontiert.

Nach dem ganzen Grund, warum setTimeout() funktioniert, während das Fehlen von einem nicht funktioniert, sollte die Verzögerung keinen Effekt auf die Funktionsweise haben, da setTimeout() hauptsächlich die Warteschlangenreihenfolge ändern soll hier, NICHT Dinge zu verzögern .

Also, warum funktioniert es nicht mit Verzögerung 0 oder 1 in Firefox, funktioniert aber wie erwartet mit Verzögerung 500 (und funktioniert mit jeder Verzögerung bei Internet Explorer 8 / Chrome)?

UPDATE: Zusätzlich zum Quellcode habe ich auch ein JSFiddle erstellt. Aber aus irgendeinem Grund weigert sich JSFiddle sogar, meinen Internet Explorer 8 zu laden, daher ist für diesen Test der folgende Code erforderlich.

UPDATE2: Jemand hat die Möglichkeit eines Problems mit der Konfigurationseinstellung dom.min_timeout_value in Firefox angesprochen. Ich habe es von 4 bis 0 bearbeitet, den Browser neu gestartet, und nichts wurde behoben. Es schlägt immer noch mit einem Timeout von 0 oder 1 fehl und ist mit 500 erfolgreich.

Hier ist mein Quellcode - ich habe ihn einfach in eine HTML-Datei auf Laufwerk C: gespeichert und in allen drei Browsern geöffnet:

%Vor%

Um zu testen, müssen Sie die Grenzen der verschachtelten Schleife für Internet Explorer 8 auf 300/100/100 ändern. oder zu 1000/1000/500 für Chrome, wegen der unterschiedlichen Empfindlichkeit von "dieser JS dauert zu lange" -Fehler gekoppelt mit JavaScript-Motordrehzahl.

    
DVK 23.12.2013, 16:41
quelle

3 Antworten

4

Es gibt eine Kopie der aktuellen (28. Juni 2016) Implementierung von window.setTimeout() in Ubuntu.

Wie wir sehen können, wird der Timer durch diese Codezeile eingefügt:

%Vor%

Dann eine paar Zeilen unter Ihnen haben eine if() Anweisung:

%Vor%

Der insertedInfo == mTimeouts.Elements() überprüft, ob der Timer, der gerade eingefügt wurde, bereits abgelaufen ist. Der folgende Block führt die angehängte Funktion NICHT aus, aber die Hauptschleife bemerkt sofort, dass ein Timer abgelaufen ist und überspringt somit den IDLE-Zustand (eine Ausbeute der CPU), den Sie erwarten.

Dies erklärt (zumindest für mich) das Verhalten, das Sie erleben. Das Rendern auf dem Bildschirm ist ein anderer Prozess (Task / Thread) und die CPU muss für diesen anderen Prozess freigegeben werden, um eine Chance zu erhalten, den Bildschirm neu zu malen. Dazu müssen Sie lange genug warten , damit Ihre Timer-Funktion nicht sofort ausgeführt wird und eine Ausbeute eintritt.

Wie Sie bemerkt haben, ist eine Pause von 500ms der Trick. Sie können wahrscheinlich eine kleinere Nummer verwenden, z. B. 50ms. So oder so wird es nicht garantieren, dass ein Ertrag eintritt, aber es wird wahrscheinlich passieren, dass der Computer, auf dem dieser Code läuft, momentan nicht überlastet ist (dh ein Anti-Virus läuft derzeit nicht im Hintergrund). )

Die komplette Funktion SetTimeout() von Firefox:

(Speicherort der Datei in der Quelle: dom/workers/WorkerPrivate.cpp )

%Vor%

WICHTIGER HINWEIS: Die JavaScript-Anweisung yield hat nichts damit zu tun, wovon ich spreche. Ich spreche von der Funktionalität sched_yield () , die auftritt, wenn ein Binärprozess bestimmte Funktionen aufruft B. sched_yield() selbst, poll() , select() usw.

    
Alexis Wilke 29.06.2016, 04:01
quelle
1

Ich habe dieses Problem mit Firefox beim Umschalten von CSS-Klassen mit jQuery zur Steuerung eines CSS-Übergangs konfrontiert.

Die Dauer von setTimeout von 0 auf 50 zu erhöhen half, aber Alexis schlug vor, dass dies nicht 100% zuverlässig war.

Die beste (falls langwierige) Lösung, die ich gefunden habe, bestand darin, einen Intervalltimer mit einer IF-Anweisung zu kombinieren, um zu überprüfen, ob die erforderlichen Stile vor dem Auslösen des Übergangs angewendet wurden, statt setTimeout zu verwenden und anzunehmen, dass die Ausführung in der vorgesehenen Reihenfolge stattgefunden hat zB

%Vor%

In meinem Fall scheint das zumindest jedes Mal in Firefox zu funktionieren.

    
Matt Saunders 11.08.2017 09:14
quelle
0

In Firefox ist der Minimalwert für setTimeout () konfigurierbar und standardmäßig 4 in aktuellen Versionen:

  

dom.min_timeout_value Die minimale Dauer in Millisekunden,   dass die window.setTimeout () - Funktion eine Zeitüberschreitung für festlegen kann.   Dies ist standardmäßig 4 ms (vor 10 ms). Aufrufe von setTimeout () mit a   Verzögerung kleiner als diese wird auf diesen Mindestwert geklemmt.

Werte wie 0 oder 1 sollten sich wie 4 verhalten - keine Ahnung, ob das zu Verzögerungen in Ihrem Code führen oder einfach nur brechen wird.

    
Álvaro González 23.12.2013 16:54
quelle