RobRobson: Probleme eine eigene CallBack Funktion zu erstellen

Hallo,

ich hab zwei Funktionen die Nacheinander aufgerufen werden.

  
function getGeoCode() { <..hole geodaten> }  
  
$.post('index.php',{ geodaten:geodaten },function(data){ $('#resultat').html(data); });  
});

da JS aber asyncron und nicht linear arbeitet, startet $.post schon bevor getGeoCode die GeoDaten ermittelt hat. Ich hab versucht getGeoCode() in eine CallBack-Funktion umzubauen und an die Stelle von "geodaten" ins $.post einzubauen. Leider klappt es nicht. Ist das der richige Weg oder macht mans besser anders?

Danke und Grüße,
Rob

  1. Lieber RobRobson,

    $.post('index.php',{ geodaten:geodaten },function(data){ $('#resultat').html(data); }); });

    dieser Aufruf setzt eine in diesem Kontext bekannte Variable "geodaten" voraus. Anstatt ihrer könntest Du auch den Funktionsaufruf selbst notieren:

    $.post('index.php',{ geodaten:getGeoCode() },function(data){ $('#resultat').html(data); }); });

    Ansonsten kenne ich die post-Methode ("Funktion") des $-Objektes nicht und weiß daher nicht, welche Parameter sie erwartet, und ob Du dabei alles richtig gemacht hast.

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
    1. Hi Felix,

      Lieber RobRobson,

      wer behauptet das?  :)

      $.post('index.php',{ geodaten:getGeoCode() },function(data){ $('#resultat').html(data); }); });

      Jop, klappt!
      Mein Versuch sah so aus: $.post('index.php',{ geodaten:function getGeoCode() {} },function(data){ $('#resultat').html(data); }); });
      War Quatsch.

      Liebe Grüße,

      Felix Riesterer.

      Danke und Viele Grüße,
      Rob

      1. Lieber RobRobson,

        Mein Versuch sah so aus: $.post('index.php',{ geodaten:function getGeoCode() {} },function(data){ $('#resultat').html(data); }); });
        War Quatsch.

        hast Du auch begriffen, warum das Quatsch war? Du wolltest ein Funktionsobjekt als Wert an das serverseitige Script übergeben. Das kann nicht klappen, da ein Funktionsobjekt kein Datentyp ist, der sich mittels HTTP sinnvoll übertragen ließe. Dass Du dieser als Lambda-Funktion im JSON-Stil notierten Funktion auch noch einen bereits vergebenen Namen neu zuweist, sieht nocheinmal mehr nach Quatsch aus (bin mir jetzt nicht sicher, aber damit solltest Du "getGeoCode" nach Deinem $.post()-Aufruf erfolgreich zerstört haben).

        Mir drängt sich der starke Verdacht auf, dass Du noch erheblichen Nachholbedarf in den Grundlagen von JavaScript hast. Das Nutzen eines Frameworks wie jQuery ist noch kein Garant für erfolgreiches Schreiben komplexer JS-Funktionalitäten!

        Liebe Grüße,

        Felix Riesterer.

        --
        ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
      2. Ich hab grade noch einen guten Hinweis gelesen.
        Kann es sein das die Lösung ist, das in der function getUserCoord()

          	myLocLatLng = results[0].geometry.location.toString();  
          	alert( "drinnen" + myLocLatLng );  
        
          
        Einen notifier aufrufen mmus, dass die GeoCodierung erfolgreich war und der das Suchergebniss zurückgibt, hier also an Stelle des alert() eine weitere Funktion aufrufe.  
          
        Also etwa so: (jetzt ohne den Kontext des Codes im vorPost)  
        ~~~javascript
          
        function FooSuche ()  
        {  
          var ort = 'deutschland';  
          getUserCoord( ort , 'FooSuche' );  //FooSuche -> Die Funktion die bei Erfolg aufgerufen werden soll  
          
          if (argument[0]) {  ..tue was mit dem ergebniss von getUserCoord().. }  
        }  
          
          
        function getUserCoord( ort , callbackfunction )  
        {  
                var myLocLatLng = false;  
                geocoder.geocode( { "address": suche_ort  }, function(results, status) {  
                        if (status == google.maps.GeocoderStatus.OK) {  
                                map.setCenter(results[0].geometry.location);  
                                var marker = new google.maps.Marker({  
                                        map: map,  
                                        position: results[0].geometry.location  
                                });  
                                myLocLatLng = results[0].geometry.location.toString();  
                                eval( callbackfunction+"(" +myLocLatLng+ ")");   //hab ich jetzt noch nicht probiert, aber halt die übergebene Funktion aufrufen  
                        }  
                });  
                return myLocLatLng;  
        }  
        
        

        Viele Grüße,
        Rob

    2. Hi nochmal,

      ich war erst geblendet, mir fiel eben erst auf das es leider noch nicht so funktioniert wie es soll.

      Ein ~~~javascript

      myLocLatLng = false;  //global! Wird in getGeoCode() geändert
      $.post('index.php',{ geodaten:getGeoCode() },function(data){ $('#resultat').html(data); }); });

      Gibt in der index.php $\_POST[myLocLatLng] = false; zurück.  
      Ergo hat der $.post-Aufruf nicht auf die Funktion gewartet. :-/  
        
        
      Viele Grüße,  
      Rob
      
      1. Lieber RobRobson,

        myLocLatLng = false; //global! Wird in getGeoCode() geändert

        das ist für mich ein ganz wesentlicher Design-Fehler. in JavaScript sind globale Variablen in der Regel ein Fehler, es sei denn sie bezeichnen größere Objekte, wie beispielsweise das "$"-Objekt.

        $.post('index.php',{ geodaten:getGeoCode() },function(data){ $('#resultat').html(data); }); });

        
        > Gibt in der index.php $\_POST[myLocLatLng] = false; zurück.  
        > Ergo hat der $.post-Aufruf nicht auf die Funktion gewartet. :-/  
          
        Und wieder mal ein klassischer Fall von "relevante Informationen zurückgehalten". Warum hast Du diese Umstände (globale Variable, was tut getGeoCode) nicht sofort in Deinem Eröffnungspost geschrieben?  
          
        Es ist ja wohl offensichtlich (auch wenn Du den Code der Funktion noch immer krampfhaft verheimlichst), dass die Funktion getGeoCode keinen Rückgabewert liefert, sondern lediglich eine globale Variable beschreibt. Wieso kannst Du also behaupten, dass Dein $.post()-Aufruf nicht auf getGeoCode "gewartet" hätte?  
          
        Und nocheinmal: Wo erwartest Du eine asynchrone Verarbeitung? Ist etwa in getGeoCode ein AJAX-Aufruf enthalten, den es zuerst abzuwarten gilt, bevor Deine Geodaten zum Versenden überhaupt bereit sind? Das wäre das Einzige, das mir in diesem Zusammenhang einleuchtet, warum Du hier auf "asynchron" bestehst.  
          
        Liebe Grüße,  
          
        Felix Riesterer.
        
        -- 
        ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
        
        1. Hi,

          Lieber RobRobson,

          Und nocheinmal: Wo erwartest Du eine asynchrone Verarbeitung? Ist etwa in getGeoCode ein AJAX-Aufruf enthalten, den es zuerst abzuwarten gilt, bevor Deine Geodaten zum Versenden überhaupt bereit sind? Das wäre das Einzige, das mir in diesem Zusammenhang einleuchtet, warum Du hier auf "asynchron" bestehst.

          Was auch der Grund ist warum ich es auch am Anfang im OP erwähnte.

          Meine Intention keinen Code zu posten war, euch lesezeit und arbeit zu ersparen. Aber ich liefere diesen auch gerne nach. Mittlerweile hab ich mich vom einfachen einsetzen in $.post() weggbewegt und will die ganze Arbeit mit der Location in ein Objekt kapseln.

            
            
          	var UserLocation = new UserLocation_obj;  
          	UserLocation.getLatLngGoogle();  
            
            
          function UserLocation_obj()  //Konstruktor  
          {  
          	this.aktSuchOrt 			= false;  
          	this.AktSuchOrtLatLng			= false;  
          }  
            
          UserLocation_obj.prototype.getAktSuchOrt	= function () 		{ return this.aktSuchOrt 	}  
          UserLocation_obj.prototype.setAktSuchOrt	= function (str)	{ this.aktSuchOrt = str; 	}  
          UserLocation_obj.prototype.getAktSuchOrtLatLng	= function () 		{ return this.AktSuchOrtLatLng 	}  
          UserLocation_obj.prototype.setAktSuchOrtLatLng	= function (obj)	{ this.AktSuchOrtLatLng = obj; 	}  
            
            
          UserLocation_obj.prototype.getLatLngGoogle= function(eventObjekt)  
          {  
          	this.setAktSuchOrt( $("#suche_ort").val() );  
          	this.setAktSuchOrtLatLng( getUserCoord( this.getAktSuchOrt() ) );  
          	alert( "drausse" + this.getAktSuchOrtLatLng( ) );  
          }  
            
            
          function getUserCoord( suche_ort ) //Google adapt  
          {  
          	var myLocLatLng = false;  
          	geocoder.geocode( { "address": suche_ort  }, function(results, status) {  
          		if (status == google.maps.GeocoderStatus.OK) {  
          			map.setCenter(results[0].geometry.location);  
          			var marker = new google.maps.Marker({  
          				map: map,  
          				position: results[0].geometry.location  
          			});  
          			myLocLatLng = results[0].geometry.location.toString();  
          			alert( "drinnen" + myLocLatLng );  
          		}  
          	});  
          	return myLocLatLng;  
          }  
          
          

          (Das drinnen-alert gibt richtig die gesuchten Koordinaten aus das drausen-alert immer nur false. Also den default-Wert in getUserCoord() )

          Mein Problem ist nach wie vor das eine asyncrone Funktion vorliegt und das ich auf das Ergebniss dieser warten muss. Wie macht man das? (its all about events?)

          Liebe Grüße,

          Felix Riesterer.

          Danke, viele Grüße, gutn Rutsch und frohes Neues Jahr ;)
          Rob

          1. Lieber RobRobson,

            UserLocation_obj.prototype.getLatLngGoogle= function(eventObjekt)
            {
            this.setAktSuchOrt( $("#suche_ort").val() );
            this.setAktSuchOrtLatLng( getUserCoord( this.getAktSuchOrt() ) );
            alert( "drausse" + this.getAktSuchOrtLatLng( ) );
            }

            Du übermittelst hier ein "eventObjekt", machst im späteren Verlauf aber keinen Gebrauch davon. Warum nicht? Welcher Art sollte das Event denn gewesen sein ("click", "mousemove" etc), und wie muss die dazu passende Reaktion sein?

            Betrachten wir einmal den Funktionsmechanismus an sich:
                1.) this.setAktSuchOrt(...); // Hier passiert irgendwas.

            2.) this.setAktSuchOrtLatLang(getUserCoord(...)); // Hier soll die Funktion
                    "getUserCoord" einen Wert zurückliefern, und zwar _sofort_!

            3.) alert(...) // Hier soll der von getUserCoord ermittelte Wert
                    ausgegeben werden.

            Soweit klar? Nun gibt es in getUserCoord aber nur eine Callback-Funktion, deren Aufruf irgendwann einmal stattfinden wird, und der dann erst den gewünschten Wert mitbringen wird. Das passiert wohl über das "geocoder"-Objekt, dessen Methode "geocode" einen Request an Google absetzt, der dann irgendwann beantwortet wird, um dann die gewünschten Geo-Koordinaten zu erhalten.

            function getUserCoord( suche_ort ) //Google adapt
            {
            var myLocLatLng = false;

            Hier definierst Du eine lokale Variable mit dem Wert false.

            geocoder.geocode( { "address": suche_ort  }, function(results, status) { ... } );

            Hier wird der Request vorbereitet, eine Callback-Funktion definiert, und das Ganze abgeschickt.

            return myLocLatLng;

            Hier hat Deine lokale Variable noch immer false als Wert. Das geocoder-Objekt hat in diesem Moment noch keine Antwort von Google erhalten, die in diese Variable geschrieben werden könnte.

            (Das drinnen-alert gibt richtig die gesuchten Koordinaten aus

            Logisch, denn es wird dann ausgeführt, wenn von Google eine Antwort kam und somit die Callback-Funktion ausgeführt wurde, in der dieser alert ja steht.

            das drausen-alert immer nur false.

            Es wird auch vor der Google-Antwort ausgeführt, weil es eben nicht in der Callback-Funktion steht.

            Mein Problem ist nach wie vor das eine asyncrone Funktion vorliegt und das ich auf das Ergebniss dieser warten muss.

            Nein, Dein Problem ist, dass Du die weitere Verarbeitung Deines Scripts von einer Callback-Funktion erledigen lassen musst, weil Deine Funktion selbst keine Antwort auf Deine Frage ("Geo-Koordinaten?") geben kann.

            Wie macht man das? (its all about events?)

            Ja, es ist "all about events", indem Du nämlich die Callback-Funktion dahingehend umbauen musst, dass sie Dir die ermittelten Koordinaten in Dein Objekt schreibt. Das tut sie nur auf ein Ereignis hin, nämlich dann, wenn Googles Antwort eingetroffen ist. Die löst nämlich eine Status-Veränderung im XHR-Objekt aus, welches geocoder.geocode() erzeugt hat. Und das auf diese Änderung hin ausgelöste Event ist dasjenige, welches Deine Callback-Funktion (mehrfach!) auslöst. Erst, wenn der Status den passenden Wert hat, reagiert Deine Callback-Funktion überhaupt mit Deinem Code (if-Statement). Ansonsten lässt sie die Statusänderungen uninteressiert über sich ergehen.

            Lass mich mal folgendes probieren:

            UserLocation_obj.prototype.getLatLngGoogle = function()  
            {  
                var t = this; // wird benötigt, damit die Referenz auf unser Objekt  
                              // erhalten bleibt, da sich "this" in der Callback-  
                              // Funktion ändern wird  
              
                t.setAktSuchOrt( $("#suche_ort").val() );  
              
                geocoder.geocode(  
                    // Objekt mit Suchdaten  
                    {  
                        "address": suche_ort  
                    },  
              
                    // Callback-Funktion  
                    function (results, status)  
                    {  
                        if (status == google.maps.GeocoderStatus.OK)  
                        {  
                            map.setCenter(results[0].geometry.location);  
                            var marker = new google.maps.Marker({  
                                map: map,  
                                position: results[0].geometry.location  
                            });  
              
                            // Wir haben die Antwort!  
                            t.setAktSuchOrtLatLng( // "t" ist noch bekannt (ist eine closure)  
                                results[0].geometry.location.toString()  
                            );  
              
                            // Daten an den Server posten  
                            $.post(  
                                'index.php',  
                                 {  
                                     // alternativ ginge hier auch results[0].geometry... etc.  
                                     geodaten : t.getAktSuchOrtLatLng()  
                                 },  
                                 function (data) {  
                                     $('#resultat').html(data);  
                                 }  
                            );  
                        }  
                    }  
                );  
            }
            

            Ich habe obigen Code natürlich nicht getestet. Das überlasse ich Dir. Aber Du siehst, wie die Callback-Funktion nun die weitere Verarbeitung übernimmt, ohne dass Du die bisherige Funktion "getUserCoord" noch bräuchtest.

            gutn Rutsch und frohes Neues Jahr ;)

            Dir auch einen guten Rutsch.

            Liebe Grüße,

            Felix Riesterer.

            --
            ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
            1. Hi Felix und schonmal vorweg vielen Dank für die ausführliche Antwort,

              Du übermittelst hier ein "eventObjekt", machst im späteren Verlauf aber keinen Gebrauch davon. Warum nicht? Welcher Art sollte das Event denn gewesen sein ("click", "mousemove" etc), und wie muss die dazu passende Reaktion sein?

              Ja, sorry, das war ein C&P Fehler. Vom EventTest übrig gbelieben mit den ich rumgespielt hatte.

              [...]

              Ich habe obigen Code natürlich nicht getestet. Das überlasse ich Dir. Aber Du siehst, wie die Callback-Funktion nun die weitere Verarbeitung übernimmt, ohne dass Du die bisherige Funktion "getUserCoord" noch bräuchtest.

              Meine Version mit der extraFunktion und der Übergabe einer beliebigen CallBack-Funktion an die das Suchergebnis weitergegeben wird, hatte auch geklappt. Ich bin aber nach dem Einbau Deiner Funktion auch bei ihr geblieben. Da sie kürzer und simpler ist. Später wird der $.post(..)-Teil nochmal in eine seperate Funktion wandern, aber erstmal funktionierts ja so und es besteht noch keine Notwenigkeit dafür.

              Also besten Dank dafür nochmal ;)

              Viele Grüße,
              Rob

  2. Hallo RobRobson,

    was macht getGeoCode bzw. wie holt es die Geodaten?

    Gruß, Jürgen

  3. Ich würde das nicht als asynchron bezeichnen.
    Code wird dann ausgeführt wenn er aufgerufen wird.
    $.post ... steht direkt da, der wird dann ausgeführt wenn das Script geladen wurde. Also praktisch "gleich".
    getGeoCode ist eine Funktion, die wird dann ausgeführt wenn sie aufgerufen wird und das wird sie bei dir nirgends.

    Callback ist, wenn eine Funktion nach der Ausführung von etwas anderem aufgerufen werden soll. Du willst das ja aber vor dem Aufruf von $.post ausführen.
    Zum Verständnis (alternativ zu Felix' Vorschlag): Du könntest auch vor der Zeile mit $.post die Funktion getGeoCode aufrufen. Wenn die Variable dann passend gesetzt wird, müssts gehen.

    1. Hi Encoder,

      Ich würde das nicht als asynchron bezeichnen.

      Ich glaube mich zu errinern das JS als asyncron bez. wird, da der Functionsaufruf unabhäng von seiner Antwort ist (= asyncron). Bei linearen Sprachen führtst Du funcktion erg = a() aus und kannst mit b(erg) gleich weiter arbeiten. bei JS geht das. Genau da lag mein Problem. ;)

      Zum Verständnis (alternativ zu Felix' Vorschlag): Du könntest auch vor der Zeile mit $.post die Funktion getGeoCode aufrufen. Wenn die Variable dann passend gesetzt wird, müssts gehen.

      ne, eben grade nicht, siehe oben. getGeoCode() behmüht ja auch weitere google maps funktionen die ebenfalls 'asyncron' (imo) sind.
      Meiner erster Gedanke war auch das in $.post einfach reinzuschreiben. dummerweise hatte ich noch ein function davor. damit klappte es nicht. Felix' Ansatz klappt jetzt.

      Danke und Grüße,
      Rob

      1. Hallo RobRobson,

        Ich glaube mich zu errinern das JS als asyncron bez. wird, da der Functionsaufruf unabhäng von seiner Antwort ist (= asyncron).

        nein, Javascript arbeitet synchron. Aber einige Techniken können asynchron arbeiten. HTTP-Requests (AJAX) können wahlweise sysnchron oder auch asynchron arbeiten, setTimeout oder setInterval läuft immer asynchron.

        Meine Frage "was macht getGeoCode bzw. wie holt es die Geodaten?" zielte genau in diese Richtung.

        Gruß, Jürgen

      2. Es wäre ja ziemlich unsinnig wenn JS komplett asynchron wäre.
        Eine Funktion ruft man meistens auf weil man will das genau jetzt etwas passiert und das dann auch fertig ist, bevor der nächste Aufruf passiert.
        Ohne diese Annahme wäre Programmcode nicht mehr lesbar oder verstehbar.

        Das asynchrone sind Ausnahmen, z.B. AJAX woman nicht weiß ob und wann die Antwort kommt, dann wird der Rest des Codes nicht gebremst.

        1. Hi,

          Das asynchrone sind Ausnahmen, z.B. AJAX woman nicht weiß ob und wann die Antwort kommt, dann wird der Rest des Codes nicht gebremst.

          Ahh ok, ich dachte das wäre generell so. Aber in meinem Beispiel ist leider genau das, das Problem.
          Siehe meine Antwort auf Felix

          Viele Grüße,
          Rob

          1. Ahh ok, ich dachte das wäre generell so.

            Das sagt mir, du könntest noch etwas Theorie vertragen.
            Eine Callbackfunktion wird von dir selbst erstellt. Du übergibst sie einer anderen Funktion, die etwas im Hintergrund erledigt und demnach also asynchron ist. Die Funktion läuft gleich durch, nach dem Aufruf sollte also nichts mehr kommen was Ergebnisse dieser Funktion benötigt.
            Stattdessen wird, wenn diese Funktion im Hintergrund fertig ist, die von dir definierte Callbackfunktion aufgerufen.
            In der kannst du dann die Ergebnisse auswerten.

            Also etwa so:

            function xy()
            {
            Code...
            Code...
            getGeoCode(meineCallbackfunktion);
            }

            function meineCallbackfunktion(?)
            {
              erst hier werden die Argumente ausgewertet,
              z.B. die Ausgabe gemacht.
            }

            Was da genau passiert und wie du die Ergebnisse kriegst, hängt von der Funktion getGeoCode ab.