frederikring: Eine Funktion überholt die Nächste?

Hallo!

Ich habe eine Javascript-Funktion (A), die wiederum eine andere Javascript-Funktion (B) benutzt.

In der Funktion A wird die Funktion B nun mehrmals hintereinander ausgeführt. In jedem Durchlauf von Funktion B wird eine (globale) Variable verändert die für die nächste Ausführung von Funktion B wichtig ist.

Eigentlich sollte das ja kein Problem sein, allerdings ist Funktion B sehr schwerfällig da sie Daten über eine API von einem anderem Server abruft. Es sieht für mich aber so aus als ob die Funktion A die Bs eins nach dem anderem aufruft ohne daß die vorhergehenden Bs schon abgeschlossen ist. Zumindest lässt mich das das loggen der Variable inner- und ausserhalb der Funktion B vermuten.

Kann so etws überhaupt sein? Und wenn ja, wie kann ich das Script dazu bringen erst fortzufahren, wenn die vorhergehende Funktion gänzlich abgeschlossen ist?

Im Prinzip läuft das so:

function B(){
....
console.log('inner'+variable);
....
}

function A(){
...
B();
console.log('outer'+variable);
B();
console.log('outer'+variable);
B();
console.log('outer'+variable);
...
}

In der Konsole stehen jetzt aber zuerst alle outer-Werte und dann alle inner-Werte.

Irgendeine Idee, oder mache ich gerade einen Denkfehler?

Vielen Dank!

  1. Kann so etws überhaupt sein? Und wenn ja, wie kann ich das Script dazu bringen erst fortzufahren, wenn die vorhergehende Funktion gänzlich abgeschlossen ist?

    Ja sowas gibts. Habe ich auch schon erlebt, das JS eine Funktion noch nicht abgearbeitet hat, aber schon die nächste aufruft, was bei mir Fehler verursacht hat.

    Ich habe es damals mit setTimeout() gelöst.
    100 µsek. sollten reichen.

    1. Hallo,

      Ja sowas gibts. Habe ich auch schon erlebt, das JS eine Funktion noch nicht abgearbeitet hat, aber schon die nächste aufruft, was bei mir Fehler verursacht hat.

      Hmmm...also wenn das alles sequentielle Funktionsaufrufe sind kann das eigentlich NICHT sein. Bzw, es wäre fatal WENN es so wäre - ich muss mich doch darauf verlassen, dass Funktionsaufrufe in der Reihenfolge abgearbeitet werden, in der ich sie hinschreibe!

      Solche Phänomene wie die beschriebenen deuten meistens auf eine Unterbrechung durch ein Event, durch einen Timer (setTimeout/setInterval) oder durch einen AJAX-Call hin - da wird natürlich der normale Programmfluss unterbrochen, und man muss als Programmierer das "Wiederaufsetzen" an der richtigen Stelle sicherstellen oder die Verarbeitung des events hinauszögern, bis der kritische Programmblock verlassen wurde.

      Aber eine "normale" Funktion in JavaScript MUSS eigentlich blockieren, bis sie durchlaufen wurde.

      Vielleicht bin ich auf dem Holzweg, aber JavaScript-Funktionen, die sich überholen, passen nicht in mein Weltbild ;)

      Jörg

      1. Hui viele Antworten, dankeschön!

        Genauer gesagt handelt es sich bei der "Funktion B" um das flickr.jquery-plugin, es handelt sich also tatsächlich um einen AJAX-Call. Die Sache ist die daß das Problem selbst dann auftritt wenn ich die explizit ausgewiesene Callback-Funktion des Plugins verwende, sprich diese Callback-Funktion zündet leider zu früh. In dem Code des Plugins kann ich aber leider nicht erkennen wieso dem so ist. Daher muss ich jetzt wohl eine andere Lösung suchen.

        Wenn sich jemand mit jQuery auskennt:
        Wäre es möglich dem $.getJSON-Befehl innerhalb des Plugins einen jQuery-eigenen und damit wahrscheinlich verlässlicheren Callback zu geben?

        Danke schon wieder!

        1. es handelt sich also tatsächlich um einen AJAX-Call. Die Sache ist die daß das Problem selbst dann auftritt wenn ich die explizit ausgewiesene Callback-Funktion des Plugins verwende, sprich diese Callback-Funktion zündet leider zu früh.

          Was heißt zu früh? Sind die per JSON geladenen Daten noch nicht verfügbar? Dann ist dein Problem doch ein ganz anderes als du im Ausgangsposting beschrieben hast...? Den Zusammenhang verstehe ich gerade nicht.

          Wäre es möglich dem $.getJSON-Befehl innerhalb des Plugins einen jQuery-eigenen und damit wahrscheinlich verlässlicheren Callback zu geben?

          Es handelt sich um einen Success-Handler, der feuert nur Erfolgsfalle, wenn auch was angekommen ist und als JSON verarbeitet wurde. Das ist der zuverlässigste, den du mit jQuery kriegen kannst.

          Es gibt noch andere Handler, wenn du $.ajax und options verwendest:
          http://docs.jquery.com/Ajax/jQuery.ajax#options
          Aber davon dürfte für dich nichts interessant sein, beforeSend, error und complete erfüllen nur andere Zwecke als success.
          Alternativ kannst du einen eigenen JSONP-Handler über jsonp notieren.

          Was genau passiert nicht wie erwartet? Ich glaube schon, dass der Callback korrekt ausgelöst wird, ansonsten liegt ein jQuery-Fehler vor und du solltest mal ein Beispiel posten, sodass wir das untersuchen können.

          Mathias

        2. Hi!

          Schonmal ueberlegt, das Asynchrone Abfragen in Deinem Fall gar nicht sind, was Du willst? Das synchron zu machen kommt wohl nicht in Frage, oder?

          --
          "Die Diebesgilde beklagte sich darueber, dass Mumm in aller Oeffentlichkeit behauptet hatte, hinter den meisten Diebstaehlen steckten Diebe."
                - T. Pratchett
  2. In der Funktion A wird die Funktion B nun mehrmals hintereinander ausgeführt. In jedem Durchlauf von Funktion B wird eine (globale) Variable verändert die für die nächste Ausführung von Funktion B wichtig ist.

    Nur am Rande: Globale Variablen sind böse. Vermeide sie so gut es geht (in JavaScript nicht immer möglich).

    Eigentlich sollte das ja kein Problem sein, allerdings ist Funktion B sehr schwerfällig da sie Daten über eine API von einem anderem Server abruft. Es sieht für mich aber so aus als ob die Funktion A die Bs eins nach dem anderem aufruft ohne daß die vorhergehenden Bs schon abgeschlossen ist. Zumindest lässt mich das das loggen der Variable inner- und ausserhalb der Funktion B vermuten.

    Wie geschieht das genau (in Funktion B)? Wenn es ein AJAX-Request ist, dann kann das durchaus sein.

    Kann so etws überhaupt sein? Und wenn ja, wie kann ich das Script dazu bringen erst fortzufahren, wenn die vorhergehende Funktion gänzlich abgeschlossen ist?

    Wenn meine Vermutung (AJAX) zutrifft, müsstest du das über den Handler machen, der ausgeführt wird, sobald sich der Status des entsprechenden XMLHttpRequest-Objekts ändert.

    --
    Reden ist Silber, Schweigen ist Gold, meine Ausführungen sind Platin.
    Self-Code: sh:( ch:? rl:( br:> n4:( ie:{ mo:) va:) de:> zu:} fl:| ss:| ls:~ js:|
  3. Kann so etws überhaupt sein?

    Nein. So allgemein ist das nicht möglich. Dein abstraktes Beispiel wird immer nacheinander ausgeführt. Das Problem liegt höchstwahrscheinlich in dem Code, den du durch »...« ersetzt hast.

    Wenn du Funktionen synchron und nacheinander startest, werden sie auch in genau der Reihenfolge ausgeführt. Ich vermute allerdings, dass irgendwo eine Asynchronität hineinkommt, wodurch die Ausführung von Code erst nach der Abarbeitung des aktuellen Threads erfolgt. Das kann durch ein setTimeout irgendwo geschehen, durch Ajax (XMLHttpRequest), durch das Laden eines Scriptes usw.

    Wenn du also »Daten über eine API von einem anderem Server abrufst«, wird mit Sicherheit irgendwo asynchron, d.h. Event- und Callback-basiert gearbeitet. XMLHttpRequest arbeitet standardmäßig asynchron und ruft Handlerfunktionen auf. Erst wenn die gefeuert werden und der Request den Status »fertig« hat, kannst du auf die Serverrückgabe zugreifen und damit weiterarbeiten.

    Du kannst also nicht notieren:

    starteRequest(); func1(); starteRequest() func2(); starteRequest(); func3();

    (Zumindest nicht mit asynchronem XMLHttpRequest, höchstens mit synchronem, das kann man Einstellen beim Erzeugen eines Requests. Allerdings friert der gesamte Browser für die Zeit eines synchronen Requests ein, das ist auch keine gute Idee, wenn man mehrere Requests nacheinander senden will.)

    Vielmehr müsste man mit Handlerfunktionen arbeiten. Schematisch:

    starteRequest(handler1);

    function handler1 () {
       starteReqest(handler2);
    }

    function handler2 () {
       starteRequest(handler3);
    }

    function handler3 () {
       usw.
    }

    Das sieht jetzt kompliziert aus, aber sinnvollerweise kann man z.B. eine Funktion schreiben, die als Handler für alle fungiert und die dann den jeweils folgenden Schritt ausführt.

    Das ist alles sehr allgemein gehalten; du solltest uns näheres darüber erzählen, was dein Code im Detail macht.

    Mathias