Fetz: Probleme mit setTimeout in Schleife

Hallo,

Ich sitze an einem kleinen persönlichen Greasemonkey-Projekt und komme einfach nicht weiter. Jetzt habe ich aus Code mal die Stelle lokalisiert und entsprechend vereinfacht, an der ich nicht weiterkomme. Dabei hat sich herausgestellt, dass es kein Greasemonkey-Problem ist, sondern, dass ich selbst trotz zahlreicher  Beispielcodes für Uhren etc. wohl ein Verständnisproblem mit setTimeout habe. Hier der "Problemcode".

var i,j;
test();

function Hinweis () {
  alert(i);
  alert(j);
}

function test() {
  for (i = 0; i < 1; i++) {
    for (j = 0; j < 2; j++) {
      window.setTimeout("Hinweis()",1000);
    }
  }
}

Das Ergebnis:
-------------
Es kommen hintereinander vier Popups mit den Anzeigen 1, 2, 1, 2
Gewünscht wäre: 0, 0, 0, 1

Irgendwie scheinen die Zählvariablen bereits vor dem durchlaufen des ersten alert hochgezählt zu sein...

Natürlich könnte man das so umstellen, dass es zu den Beispielcodes für Uhren etc. passt, aber dann passt es nicht zu meinem Greasemonkey-Projekt, in dem in bestimmten Abständen eine Funktion mit Übergabe von Zählvariablen aufgerufen werden muss.

Vorab herzlichen Dank.

Gruß, Fetz

  1. Hi,

    Irgendwie scheinen die Zählvariablen bereits vor dem durchlaufen des ersten alert hochgezählt zu sein...

    Ja, natuerlich.
    setTimeout "wartet" nicht auf irgendetwas, sondern setzt lediglich einen "Termin" in einen internen "Kalender", "in x Millisekunden tue dies und das".
    Deine zwei verschachtelten Schleifen laufen also in Nullkommanix durch - anschliessend hat i den Wert 1 und j den Wert 2.
    Erst Aeonen spaeter wird dann deine Funktion Hinweis ein paar mal aufgerufen - und kann dann nur noch die aktuellen Werte von i und j, die seit nahezu Ewigkeiten feststehen, ausgeben.

    Vorab herzlichen Dank.

    Wofuer eigentlich - hast du etwa eine Frage? :-)

    Loesen kannst du so etwas bspw. ueber Closures, die Variablen in einem eigenen kleinen "Mikrokosmos" "konservieren", so dass die zum Zeitpunkt der Ausfuherung aktuellen Werte auch spaeter noch zur Verfuegung stehen.

    MfG ChrisB

  2. Hallo,

    [...], sondern, dass ich selbst trotz zahlreicher  Beispielcodes für Uhren etc. wohl ein Verständnisproblem mit setTimeout habe. Hier der "Problemcode".

    der Artikel Komfortable Timer-Funktion von Struppi und Mathias Schäfer könnte für Dich interessant sein.

    Freundliche Grüße

    Vinzenz

  3. Hallo,

    erstmal Danke für die hilfreichen Beiträge und das Verstehen meiner Frage, obwohl ich vergessen habe, diese zu stellen :-)

    »»Closures werden damit zu einem Allround-Werkzeug in der fortgeschrittenen JavaScript-Programmierung

    Ich fühle mich ja geehrt, dass jemand den Eindruck hat, dass mir das hilft. Ich glaube auch in der Tat den Fehler in meinem Code verstanden zu haben. Ich sehe aber im Closure-Ansatz trotzdem noch nicht die Lösung zu meinem Problem (vermutlich überfordert mich eben doch eine fortgeschrittenen JavaScript-Programmierung).

    Der zweite Link mit der Timer-Funktion gefällt mir spontan. Allerdings habe ich mit Greasemonkey wohl keine Möglichkeit, einen Parameter im Funktionsaufruf der "geschleift" wird mitzugeben
    (siehe http://www.oreillynet.com/pub/a/network/2005/11/01/avoid-common-greasemonkey-pitfalls.html?page=3)

    Hat jemand noch einen Tipp, wie man das hinbekommen kann?

    Danke und Gruß,

    Fetz

    1. Der zweite Link mit der Timer-Funktion gefällt mir spontan. Allerdings habe ich mit Greasemonkey wohl keine Möglichkeit, einen Parameter im Funktionsaufruf der "geschleift" wird mitzugeben
      (siehe http://www.oreillynet.com/pub/a/network/2005/11/01/avoid-common-greasemonkey-pitfalls.html?page=3)

      Warum nicht? Auf der verlinkten Seite wird keine anonyme Funktion gezeigt, die tauchen dort seltsamerweise gar nicht auf.

      Hat jemand noch einen Tipp, wie man das hinbekommen kann?

      Die Frage ist was?
      Dei Skript hat ja mehrere Fehler insofern läßt sihc gar nciht sagen was du genau willst.

      Struppi.

      1. Warum nicht? Auf der verlinkten Seite wird keine anonyme Funktion gezeigt, die tauchen dort seltsamerweise gar nicht auf.

        Hallo,

        ok, ich drück es mal anders aus. Wenn ich den Tipp mit dem Timer auf mein Beispiel umsetze, sieht das etwa so aus:

        function test(i,j) {
         if (j == undefined) {
              i = 0;
           j = 0;
            }
            alert(i);
            alert(j);
            if (i < 1) {
              if (j < 1) {
               j++;
                window.setTimeout("test "+i+" "+j ,1000);
              }
           else {
                j = 0;
             i++;
          if (i < 1) {
                  window.setTimeout("test("+i+","+j+")",1000);
          }
           }
            }
          }
          test();

        Und siehe da, funktioniert als HTML-Datei mit Javascript einwandfrei. Leider ist bei Benutzung dieses Scriptes in Greasemonkey nach Ausgabe von 0,0 bereits Schluß. Meldung von Firebug: test ist nicht definiert. Und das ist eben das Problem, was im obigen Link erläutert ist. Ich rufe eine Funktion test(i,j) auf. Greasemonkey lässt aber gemäß dem Link nur einen Aufruf von test zu. Dies konnte ich mit Funktionen ohne Parameter (also test() im Vergleich mit test) experimentell bestätigen.

        Es hängt also jetzt daran, wie ich einen zeitgesteuerten Funktionsaufruf ohne Parameterübergabe mit Ausgabe von i und j hinbekomme oder wie ich bei Greasemonkey doch Parameter mitgeben kann.

        Hintergrund: Ich muss einige Links aufrufen, die sich erst dann aufrufen lassen, wenn zuvor andere Links aufgerufen wurden. Ohne setTimeout klappt das im Prinzip, nur ist es für den Server zu schnell, so dass er sich verschluckt. Mit i und j bilde ich ein zweidimensionales array aus denen ich die Parameter für die Links bilde.
        Das ganze geht auch per Hand. Nur brauche ich jedesmal eine dreiviertel Stunde, bis ich alles zusammengeklickt habe. Mit Greasemonkey wäre es automatisiert...

        Gruß, Stefan

        Hat jemand noch einen Tipp, wie man das hinbekommen kann?

        Die Frage ist was?
        Dei Skript hat ja mehrere Fehler insofern läßt sihc gar nciht sagen was du genau willst.

        Struppi.

        1. ok, ich drück es mal anders aus. Wenn ich den Tipp mit dem Timer auf mein Beispiel umsetze, sieht das etwa so aus:

          Da ist immer noch keine anonyme Funktion.

          function test(i,j) {
          if (j == undefined) {
                i = 0;
             j = 0;
              }
              alert(i);
              alert(j);
              if (i < 1) {
                if (j < 1) {
                 j++;
                  window.setTimeout("test "+i+" "+j ,1000);

          Das geht nicht.

          }
             else {
                  j = 0;
               i++;
            if (i < 1) {
                    window.setTimeout("test("+i+","+j+")",1000);

          Das müßte gehen.

          }
             }
              }
            }
            test();

          Und siehe da, funktioniert als HTML-Datei mit Javascript einwandfrei. Leider ist bei Benutzung dieses Scriptes in Greasemonkey nach Ausgabe von 0,0 bereits Schluß. Meldung von Firebug: test ist nicht definiert. Und das ist eben das Problem, was im obigen Link erläutert ist. Ich rufe eine Funktion test(i,j) auf. Greasemonkey lässt aber gemäß dem Link nur einen Aufruf von test zu. Dies konnte ich mit Funktionen ohne Parameter (also test() im Vergleich mit test) experimentell bestätigen.

          Ich kenn mich mit Greasemonkey nicht so aus, aber das Verhalten ist nicht erklärbar, die Fehlermledung müßte anders lauten und tut sie in einer normalen HTML Seite auch:

          Fehler: missing ; before statement
          Quelldatei: test.html
          Zeile: 77, Spalte: 5
          Quelltext:
          test 0 1

          Es hängt also jetzt daran, wie ich einen zeitgesteuerten Funktionsaufruf ohne Parameterübergabe mit Ausgabe von i und j hinbekomme oder wie ich bei Greasemonkey doch Parameter mitgeben kann.

          Mit einer anonymen Funktion z.b. so:

          function test(i,j) {  
           var callback = function(p1, p2) {  
            return function() {  
             alert(p1 + '-' + p2);  
            }  
           };  
            
           if (typeof j == 'undefined') {  
            i = 0;  
            j = 0;  
           }  
              if (i < 1) {  
            if (j < 1) {  
             j++;  
             window.setTimeout(callback(i, j) ,1000);  
            } else {  
             j = 0;  
             i++;  
             if (i < 1) {  
             window.setTimeout(callback(i, j), 1000);  
             }  
            }  
           }  
          }  
            test();  
          
          

          Die callback Funktion wird aber nur einmal aufgerufen.

          Struppi.

          1. Hallo und herzlichen Dank für die Hilfe,

            ich merke schon, dass mein Projekt zu groß für mein Hirn ist...

            window.setTimeout("test "+i+" "+j ,1000);

            Das geht nicht.

            ...selbst das posten meines Codes scheint mich zu überfordern. Obige Zeile entstammt aus einem verzweifelten Versuch, Greasemonkey evtl. doch Parameter zur Funktion unterzujubeln.
            Wenn ich statt obiger Zeile wie unten

            window.setTimeout("test("+i+","+j+")",1000);

            benutze, kommt nach Ausgabe der zwei ersten alerts (0 und 0) folgender Fehler (nur bei Benutzung in Greasemonkey):

            "test is not defined"

            Bei dem von Dir geposteten Script hätte ich spontan eine gleichartige Meldung erwartet. Dies ist jedoch nicht der Fall. Greasemonkey scheint mit einem solchen Konstrukt zurechtzukommen. Wenn ich ein bisschen dazugelernt habe, dann würde ich jetzt behaupten, dass Dein Code die Umsetzung dieses bereits genannten Closure-Prinzips ist (richtig ??).
            Was ich dann aber gar nicht mehr verstehe: Warum wird callback nur einmal aufgerufen...*buhuu/nixversteh*..?

            Die callback Funktion wird aber nur einmal aufgerufen.

            Da ich jetzt einen Stand erreicht habe, in dem ich feststellen muss, dass ich mangels Verständnis absolut nicht mehr weiterkomme, bin ich absolut nicht böse, wenn mir keiner mehr eine Lösung vorkaut.

            Zur Not kann ich mit der bisherigen Lösung ein eigenes HTML-Dokument mit javascript starten. Die Parameterwerte zum Starten des Scriptes, die ich komfortabel mit Greasemonkey hätte auslesen können, müsste ich dann halt manuell im Code eingeben. Ist immer noch deutlich besser als sich eine dreiviertelstunde durchzuklicken ;-)

            Danke und Gruß,

            Fetz

            1. Bei dem von Dir geposteten Script hätte ich spontan eine gleichartige Meldung erwartet. Dies ist jedoch nicht der Fall. Greasemonkey scheint mit einem solchen Konstrukt zurechtzukommen. Wenn ich ein bisschen dazugelernt habe, dann würde ich jetzt behaupten, dass Dein Code die Umsetzung dieses bereits genannten Closure-Prinzips ist (richtig ??).

              Ja.

              Was ich dann aber gar nicht mehr verstehe: Warum wird callback nur einmal aufgerufen...*buhuu/nixversteh*..?

              Weil deine programmierlogik so ist.

              Da ich jetzt einen Stand erreicht habe, in dem ich feststellen muss, dass ich mangels Verständnis absolut nicht mehr weiterkomme, bin ich absolut nicht böse, wenn mir keiner mehr eine Lösung vorkaut.

              Es weiß ja keiner was du machst, was ist i was ist j und was sollen diese Variabeln darstellen oder erreichen?
              So wie du es jetzt programmiert hast, wird der callback nur einmal aufgerufen.

              Mir ist noch ein Fehler meinerseits aufgefallen, die callback Funktion müßte so aussehen:

               var callback = function(p1, p2) {  
                return function() {  
                 alert(p1 + '-' + p2);  
               test(p1, p2);  
                }  
               };  
              
              

              Sie soll ja auch die test() Funktion aufrufen.

              Struppi.

            2. Hallo,

              Struppi hat recht:

              Es weiß ja keiner was du machst, was ist i was ist j und was sollen diese Variabeln darstellen oder erreichen?

              ich merke schon, dass mein Projekt zu groß für mein Hirn ist...

              Es kommt einem wirklich vor, als wüsstest du selbst nicht, was du eigentlich erreichen willst. Mit deinem ursprünglichen Code

                
              function test() {  
                for (i = 0; i < 1; i++) {  
                
                     //tu was...  
                  }  
                }  
              }
              

              wird die Variable i hochgezählt, und zwar genau ein mal, denn dann ist sie bereits nicht mehr <1. Willst du das wirklich? Wenn ja, dann hätte auch eine boolesche Variable gereicht, etwa so:

                
              var fertig = false;  
                
              function test() {  
                
                if (!fertig) {  
                
                     //tu was...  
                     fertig = true;  
                }  
              }
              

              Aber vermutlich willst du das nicht.

              Es macht m.E. wenig Sinn, blind irgendwechen Code auszuprobieren, in der Hoffenung das er vielleicht das bewirkt, von dem man selbst nicht genau weiss, ob man es so will oder nicht...

              Das Ergebnis:

              Es kommen hintereinander vier Popups mit den Anzeigen 1, 2, 1, 2
              Gewünscht wäre: 0, 0, 0, 1

              Das kannst du haben, aber ob das viel Sinn macht, bezweifle ich:

                
              function hinweis (n) {  
                alert(n);  
              }  
                
              function test() {  
                
                var i = 0;  
                var j = 0;  
                
                var inner = function () {  
                
                  if( j < 2 ) {  
                
                    hinweis(i);  
                    hinweis(j);  
                
                    window.setTimeout(inner,1000);  
                    j++;  
                
                  } else {  
                
                    if (i < 1) {  
                
                      i++;  
                    }  
                  }  
                };  
                
                inner();  
              }  
                
              test();  
              
              

              Hier passiert dasselbe mit i wie in deinem ursprünglichen Code: Es wird genau 1 mal hochgezählt und solange es 0 ist, wird j hochgezählt. Das wäre wie gesagt einfacher mit einem booeleschen Wert für i zu machen, aber ich hab's jetzt mal so umgesetzt, wie du ursprünglich, nur mit einer Closure-Funktion namens inner().

              Gruß, Don P

              1. Hallo zusammen,

                erstmal ganz herzlichen Dank. Es klappt jetzt einwandfrei!
                Es tut mir leid, falls ich unbeabsichtigt jemanden verärgert haben sollte.

                Struppi hat recht:

                Es weiß ja keiner was du machst, was ist i was ist j und was sollen diese Variabeln darstellen oder erreichen?

                Ich dachte einfach, es ist sinnvoll, nicht den ganzen Code zu posten, sondern nur den Kern meines Problems. Der fertige Code sieht an der Problemstelle jetzt etwa so aus:

                 auslesen();  
                  
                function auslesen(i,j) {  
                  var callback = function(p1, p2) {  
                    return function() {  
                      // In Rechnungsansicht gehen  
                      window.open("http://www.HomepagemeinesAnbieters.de?Rechnungsnummer=" +Rechnungsnummer[p1], "_self");  
                      // Rufnummerninformationen aufrufen  
                      window.open("http://www.HomepagemeinesAnbieters.de/Uebersicht?Rechnungsnummer=" +Rechnungsnummer[p1], "_self");  
                      // Rufnummer auswählen  
                      window.open("http://www.HomepagemeinesAnbieters.de/Uebersicht?Rechnungsnummer=" +Rechnungsnummer[p1] + "&Rufnummer=" + Rufnummern[p2] + "&MarkierteRufnummer=" + Rufnummern[p2] + "&GewaehlteNummer=true", "_blank");  
                      // Download Rufnummerninformation  
                      window.open("http://www.HomepagemeinesAnbieters.de/Uebersicht?Rechnungsnummer=" +Rechnungsnummer[p1] + "&Rufnummer=" + Rufnummern[p2] + "&exportReport=Uebersicht&ReportType=csv", "_self");  
                      // Rufnummerninformation aufrufen  
                      window.open("http://www.HomepagemeinesAnbieters.de/Details?Rechnungsnummer=" +Rechnungsnummer[p1], "_blank");  
                      // Download Einzelverbindungsnachweis  
                      window.open("http://www.HomepagemeinesAnbieters.de/Details?Rechnungsnummer=" +Rechnungsnummer[p1] + "&Rufnummer=" + Rufnummern[p2] + "&exportReport=Details&ReportTyp=csv", "_self");  
                      auslesen(p1, p2);  
                    }  
                  };  
                  
                  if (typeof j == 'undefined') {  
                    i = 0;  
                    j = -1;  
                  }  
                  if (i < (NummernProRechnung.length)) {  
                    if (j < (NummernProRechnung[i]-1)) {  
                      j++;  
                      window.setTimeout(callback(i, j) ,5000);  
                    } else {  
                      j = 0;  
                      i++;  
                      if (i < (NummernProRechnung.length)) {  
                        window.setTimeout(callback(i, j), 5000);  
                      }  
                    }  
                  }  
                } 
                

                Zuvor lese ich noch per Script die entsprechenden Rechnungsnummern für den entsprechenden Rechnungszeitraum aus (daher Greasemonkey). Außerdem liegen Arrays vor, in denen die Rechnungskonten und die Rufnummern drinstehen.

                Es kommt einem wirklich vor, als wüsstest du selbst nicht, was du eigentlich erreichen willst. Mit deinem ursprünglichen Code

                Durchaus möglich, dass mein Kurs zum Ziel eher ein Slalomkurs als eine gerade Linie war. Das liegt einfach daran, dass ich (mehr oder weniger) für jede Codezeile einmal im selfhtml-Archiv recherchieren muss.
                Ich kann mir auch vorstellen, dass beim Anblick meines Codes zahlreiche Leute eine viel bessere und elegantere Lösung gefunden hätten.
                Allerdings bin ich mit der jetzigen Lösung zufrieden, weil ich das Gefühl habe, dass ich den Code jetzt auch wirklich verstanden habe und beim nächsten Mal darauf aufbauen kann.

                Wenn ich was dazulernen kann, bin ich aber gerne bereit, mich da tadeln zu lassen :-)

                Also nochmals ganz herzlichen Dank an Euch!

                Gruß, Fetz