Javascript: setTimeout und Schnittstelle einfrieren

9

Kontext

Ich habe ungefähr 10 komplexe Graphen, die jeweils 5sec benötigen, um zu aktualisieren. Wenn ich eine Schleife für diese 10 Diagramme mache, dauert es etwa 50 Sekunden, um zu aktualisieren. Während dieser 50 Sekunden kann der Benutzer eine Bildlaufleiste verschieben. Wenn die Bildlaufleiste verschoben wird, muss die Aktualisierung angehalten werden, und wenn die Bildlaufleiste angehalten wird, wird die Aktualisierung erneut ausgeführt.

Ich verwende die Funktion setTimeout in der Schleife, um die Schnittstelle zu aktualisieren. Der Algorithmus ist:

  • rendert das erste Diagramm
  • setTimeout (rendert das zweite Diagramm, 200)
  • wenn das zweite Diagramm gerendert wird, rendern Sie das dritte in 200ms und so weiter

Mit setTimeout können wir das Bildlaufleisten-Ereignis abfangen und die nächste Aktualisierung deaktivieren, um zu vermeiden, dass 50 Sekunden gewartet werden müssen, bevor die Bildlaufleiste verschoben wird ...

Das Problem ist, dass es nicht jederzeit ausgeführt wird.

Nehmen Sie den einfachen folgenden Code (Sie können es in dieser Geige versuchen: Ссылка ):

HTML:

%Vor%

Javascript:

%Vor%

Der Code tut:

  • Wenn Sie Ihre Maus in das rote div bewegen, wird ein Zähler aktualisiert
  • wenn Sie auf das rote div klicken, simuliert es eine große Verarbeitung von 1sec (so friert es die Oberfläche aufgrund von JavaScript-Mono-Thread ein)
  • Warten Sie nach dem Einfrieren 1 ms und resimulieren Sie die Verarbeitung und so weiter, bis Sie die Maus erneut bewegen
  • Wenn die Maus erneut bewegt wird, wird die Zeitüberschreitung unterbrochen, um eine Endlosschleife zu vermeiden.

Das Problem

Wenn Sie einmal klicken und während des Einfrierens die Maus bewegen, dachte ich, dass der nächste Code, der ausgeführt wird, wenn ein setTimeout auftritt, der Code des mousemove-Ereignisses ist (und somit das Timeout und das Einfrieren aufhebt) ) ABER manchmal gewinnt der Zähler des Klicks 2 oder mehr Punkte, anstatt nur 1 Punkt zu bekommen, aufgrund des mouvemove-Ereignisses ...

Fazit dieses Tests: Die Funktion setTimeout gibt die Ressource nicht immer zur Ausführung eines Codes während eines mousemove-Ereignisses frei, sondern behält manchmal den Thread und führt den Code innerhalb des settimeout-Callbacks aus, bevor ein anderer Code ausgeführt wird.

Dies hat zur Folge, dass der Benutzer in unserem Beispiel 10 Sekunden warten kann (2 Grafiken werden gerendert), anstatt 5 Sekunden zu warten, bevor die Bildlaufleiste verwendet wird. Das ist sehr ärgerlich und wir müssen dies vermeiden und sicherstellen, dass nur ein Graph gerendert wird (und andere abgebrochen werden), wenn die Bildlaufleiste während einer Render-Phase verschoben wird.

Wie sicher sein, das Timeout zu brechen, wenn sich die Maus bewegt?

PS: In dem einfachen Beispiel unten, wenn Sie das Timeout mit 200ms aktualisieren, läuft alles perfekt, aber es ist keine akzeptable Lösung (das eigentliche Problem ist komplexer und das Problem tritt bei einem 200ms Timer und einer komplexen Schnittstelle auf). Bitte keine Lösung als "Optimiere den Render der Graphen", das ist hier nicht das Problem.

EDIT: cernunnos hat eine bessere Erklärung des Problems: Durch "Blockieren" des Prozesses in Ihrer Schleife stellen Sie außerdem sicher, dass kein Ereignis behandelt werden kann, bis diese Schleife beendet ist. Daher wird jedes Ereignis nur zwischen der Ausführung jeder Schleife behandelt (und das Zeitlimit gelöscht) (daher auch) Sie müssen manchmal auf 2 oder mehr vollständige Ausführungen warten, bevor Sie unterbrechen) .

Das Problem ist genau in Fettschrift enthalten: Ich möchte sicher sein, die Ausführung zu unterbrechen, wenn ich möchte, und nicht 2 oder mehr vollständige Ausführungen zu warten, bevor

unterbrochen wird

Zweites EDIT:

Zusammenfassend: Nimmt dieses jsfiddle: Ссылка (der Code oben).

Aktualisieren Sie diese Datei und stellen Sie eine Lösung bereit für:

Maus bewegt sich auf dem roten div. Dann klicken und weiterfahren: der rechte Zähler muss nur einmal anheben. Aber manchmal wird es 2 oder 3 Mal erhöht, bevor der erste Zähler wieder laufen kann ... das ist das Problem, es muss nur einmal ausgelöst werden!

    
Jerome Cance 12.04.2013, 13:50
quelle

7 Antworten

1

Das GROSSE Problem hier ist setTimeout ist unberechenbar, sobald es angefangen hat, und besonders, wenn es etwas schweres Leben macht.

Sie können die Demo hier sehen: Ссылка

%Vor%

Es gibt zwei Dinge, die Sie tun können, um die Auswirkung auf die Browserleistung zu minimieren.

Speichern Sie alle intents der setTimeoutID und durchlaufen Sie sie, wenn Sie aufhören möchten

%Vor%

Setzen Sie ein Flag, wenn Sie versuchen, den Prozess zu stoppen, und prüfen Sie dieses Flag in Ihrem Worker-Thread, falls ClearTimeout den Timer nicht stoppen konnte

%Vor%

Sie können dieses neue Skript ausprobieren. Ссылка

    
Du D. 20.04.2013 16:44
quelle
0

Ich habe das Problem vielleicht verstanden, aber angenommen, Sie versuchen, die Schnittstelle nach einem Klick für mindestens 1 Sekunde zu blockieren und die Blockierung aufzuheben, indem Sie die Maus bewegen (nach dieser mindestens 1 Sekunde):

Dies ist keine gute Implementierung des Schlafes, da Sie den Prozess die ganze Zeit laufen lassen (nichts tun! = schlafen), was zu einer Verschwendung von Ressourcen führt.

Warum erstellen Sie kein Overlay (ein halb / vollständig transparentes div), legen Sie es über den Rest der Schnittstelle (Position fix, volle Breite und volle Höhe) und verwenden Sie es, um jegliche Interaktion mit der darunterliegenden Schnittstelle zu verhindern. Zerstöre es dann, wenn die Bedingungen stimmen (eine Sekunde ist vergangen und der Benutzer hat die Maus bewegt).

Dies verhält sich eher wie ein Schlaf (hat eine gewisse Verarbeitungszeit, gibt aber den Prozessor für eine bestimmte Zeit frei) und sollte Ihnen helfen, das gewünschte Verhalten zu erreichen (vorausgesetzt, ich habe es richtig verstanden).

Es hat den zusätzlichen Vorteil, dass Sie dem Benutzer einen visuellen Hinweis geben können, dass etwas verarbeitet wird.

Bearbeiten: Durch "Blockieren" des Prozesses in Ihrer Schleife stellen Sie außerdem sicher, dass kein Ereignis behandelt werden kann, bis diese Schleife beendet ist, so dass jedes Ereignis nur zwischen der Ausführung jeder Schleife behandelt (und das Zeitlimit gelöscht) wird (weshalb Sie manchmal haben) warten auf 2 oder mehr vollständige Ausführungen vor dem Unterbrechen).

    
cernunnos 12.04.2013 14:16
quelle
0

Überraschend genug, dass Sie das nicht herausgefunden haben, wenn Sie Timeout () setzen; Sie können danach einen Scheck eingeben. Eine Variable ist true, trash die Wartezeit oder trash es. Jetzt gibt es eine Methode, die Sie überprüfen können, um mit einer Bildlaufleiste zu blättern. Nachdem Sie es innerhalb einer Variablen mit den Mitteln überprüft haben, werden Sie feststellen, dass es sich beim Durchlauf des Balkens um unendlich lange Zeiten handelt und viele Ausführungszeiten von 5 Sekunden durchlaufen. Um dies zu beheben, fügen Sie eine 1 Sekunde Wartezeit hinzu, um sicherzustellen, dass es sich nicht wiederholt. Ihre Begrüßung:)

    
user2262111 17.04.2013 01:10
quelle
0

Jede lange laufende Funktion bindet Ihr Browserfenster ein. Ziehen Sie in Erwägung, Ihren complexAlgorithm () außerhalb Ihres Haupt-JavaScript-Codes zu verschieben, indem Sie WebWorker verwenden.

    
Thad Blankenship 19.04.2013 14:29
quelle
0

Die Antwort ist in Ihrer Frage

  

... die Aktualisierung muss anhalten und wenn die Bildlaufleiste aufhört sich zu bewegen, wird die   Aktualisierung erfolgt erneut.

Sie sollten complexAlgorithm so schreiben, dass Sie es fast sofort in einer Mitte bremsen können (gerade wenn Sie wissen, dass Sie erneut laufen müssen)

Der Hauptcode sollte ungefähr so ​​aussehen

%Vor%

und rendert das Diagramm in kleinen Blöcken (jede läuft & lt; 1 sec) in setTimeout wie

%Vor%

Dies ist nur eine Ideenskizze, die tatsächliche Implementierung kann komplett anders sein

    
VitaliyG 19.04.2013 17:45
quelle
0

Was Sie tun möchten, ist nicht möglich ohne Web-Arbeiter, das nur in einigen neuesten Browsern speziell Chrome implementiert ist.

Andernfalls müssen Sie Ihren Algorithmus in der Warteschlange unterbrechen. Genau wie jQuery UI setzt jede nächste Animationsrechnung in die Warteschlange. Ссылка

Es ist eine einfache Warteschlange und der nächste Befehlssatz wird mit Hilfe von setTimeout in die Warteschlange gestellt.

%Vor%

Kann in

übersetzt werden %Vor%

Dies ist nur ein Beispiel dafür, wie eine synchrone for-Schleife geschrieben werden kann, um dieselbe Logik asynchron mit einer kleineren unabhängigen Iteration auszuführen.

    
Akash Kava 21.04.2013 19:09
quelle
0

Probieren Sie die Web-Mitarbeiter aus. Ich denke, dass es in dieser Situation nützlich sein wird.

  1. Ссылка

  2. Ссылка

Jeet 22.04.2013 09:51
quelle

Tags und Links