Student2015: Block Scope Problem

Hi Leute,

ich hab folgende JavaScript Funktion, und möchte dass mein Array result, dass beim ersten console.log noch sauber mit Daten versorgt wird, diese Daten beim zweiten console.log noch hat. Wie mache ich das?

function getData() {
	var result = new Array();

    var url = "http://query.yahooapis.com/v1/public/yql";
    var symbol = 'AAPL';//$("#symbol").val();
	
	var dateInput1 = '2015-01-01';
	var dateInput2 = '2015-05-01';
	var data = encodeURIComponent("select * from yahoo.finance.historicaldata where symbol = 'YHOO' and startDate = '2009-09-11' and endDate = '2010-03-10'");
   	
    $.getJSON(url, 'q=' + data + "&format=json&diagnostics=true&env=http://datatables.org/alltables.env")
        .done(function (data) {
		
		for(var i = 0; i < data.query.results.quote.length; i++) {
		//console.log(data.query.results.quote.length)
		//console.log(data.query.results.quote[i].Close);
			result[i] = data.query.results.quote[i];
			console.log(result);
		}
        $("#result").text("Bid Price: " + data.query.results.quote.LastTradePriceOnly);
		
    })
        .fail(function (jqxhr, textStatus, error) {
        var err = textStatus + ", " + error;
            $("#result").text('Request failed: ' + err);
    });
	console.log("result",result);
	return result;
}
  1. ich hab folgende JavaScript Funktion, und möchte dass mein Array result, dass beim ersten console.log noch sauber mit Daten versorgt wird, diese Daten beim zweiten console.log noch hat. Wie mache ich das?

    Du schreibst das 2. console.log in das done-callback, dafür ist es da.

    1. ich hab folgende JavaScript Funktion, und möchte dass mein Array result, dass beim ersten console.log noch sauber mit Daten versorgt wird, diese Daten beim zweiten console.log noch hat. Wie mache ich das? Du schreibst das 2. console.log in das done-callback, dafür ist es da.

      ich möchte die daten aus dem ersten console.log, returnen. wie mache ich das? :)

      var re = getData();

      -> re soll jetzt alle daten haben die ich beim ersten console.log habe, was muss ich machen?

      1. ich möchte die daten aus dem ersten console.log, returnen. wie mache ich das? :)

        Das ist nicht möglich, weil zum Zeitpunkt wo du returnen willst, die Daten noch nicht da sind.

        var re = getData();

        -> re soll jetzt alle daten haben die ich beim ersten console.log habe, was muss ich machen?

        Entweder packst du auch alles was re nutzt mit in das Callback, oder du gibst eine Promise zurück. Für Promises gibt es auch für ältere Browser JS-Libs und sicher auch schlankere Polyfills.

        1. Entweder packst du auch alles was re nutzt mit in das Callback, oder du gibst eine Promise zurück. Für Promises gibt es auch für ältere Browser JS-Libs und sicher auch schlankere Polyfills.

          Und bei jQuery ist das xhr-Objekt selbst eine(eine? ein) Promise.

      2. Hallo,

        ich hab folgende JavaScript Funktion, und möchte dass mein Array result, dass beim ersten console.log noch sauber mit Daten versorgt wird, diese Daten beim zweiten console.log noch hat.

        du scheinst nicht zu verstehen, dass die beiden Aufrufe zeitlich in genau der umgekehrten Reihenfolge aufgerufen werden wie sie im Code stehen.

        ich möchte die daten aus dem ersten console.log, returnen. wie mache ich das? :)

        var re = getData();

        -> re soll jetzt alle daten haben die ich beim ersten console.log habe, was muss ich machen?

        Szenario: Du sitzt mit einer hübschen jungen Frau, die du in der Bar kennengelernt hast, im Zimmer deines Nobelhotels. Du greifst zum Telefon, rufst Room Service und bestellst eine Flasche Champagner, zwei Gläser und ein Schälchen Erdbeeren aufs Zimmer. Gerade in dem Moment, als du den Hörer wieder auflegst, fragt deine Begleitung schon mit einem etwas quengelnden Unterton: Wo ist denn nun der Schampus?

        Was will ich damit sagen? - Mit dem Aufruf deiner Funktion getData() erklärst du dem Browser eigentlich nur, was du haben möchtest, außerdem gibst du ihm zwei Callback-Funktionen an, die er bei Erfolg bzw. Misserfolg aufrufen soll. Die Funktion kehrt sofort zurück, es ist noch nichts passiert. Du hast bisher bloß die Bestellung aufgegeben. Ebenso, wie du vom Anrufen allein noch keinen Champagner und keine Erdbeeren da stehen hast.
        Erst in einer der beiden Callback-Funktionen kannst du auf das Ergebnis reagieren - also in done() die empfangenen Daten verarbeiten und nutzen, oder in fail() irgendwie mit dem Fehler umgehen. Dein übergeordneter Code, der getData() aufgerufen hat, ist aber inzwischen schon ganz woanders angekommen.

        So long,
         Martin

        1. Hallo Der Martin,

          Szenario: Du sitzt mit einer hübschen jungen Frau, die du in der Bar kennengelernt hast, im Zimmer deines Nobelhotels. Du greifst zum Telefon, rufst Room Service und bestellst eine Flasche Champagner, zwei Gläser und ein Schälchen Erdbeeren aufs Zimmer. Gerade in dem Moment, als du den Hörer wieder auflegst, fragt deine Begleitung schon mit einem etwas quengelnden Unterton: Wo ist denn nun der Schampus?

          Früher waren es noch Brötchen.

          Bis demnächst
          Matthias

          --
          Das Geheimnis des Könnens liegt im Wollen. (Giuseppe Mazzini)
          1. Hallo Matthias,

            Wo ist denn nun der Schampus?

            Früher waren es noch Brötchen.

            ja, ich weiß. Ich hatte den Beitrag sogar noch irgendwo im Hinterkopf präsent.
            Aber a) was geht mich mein dummes G'schwätz von neulich an? Und b) variatio delectat. ;-)

            Ich habe noch überlegt, wie ich die Callback-Funktionen in eine der Metaphern bringe, mir wollte aber keine plausible Entsprechung einfallen.

            So long,
             Martin

  2. Tach!

    Wie mache ich das?

    Das wurde ja schon genannt, ein Callback übergeben oder das Promise-Muster verwenden. Aber mal was ganz anderes:

    var data = encodeURIComponent("select * from yahoo.finance.historicaldata where symbol = 'YHOO' and startDate = '2009-09-11' and endDate = '2010-03-10'");

    $.getJSON(url, 'q=' + data + "&format=json&diagnostics=true&env=http://datatables.org/alltables.env")
    

    Du machst da ja interessante Dinge. Darf ich dir auch mal ein delete from tablename per Ajax schicken?

    dedlfix.

    1. Du machst da ja interessante Dinge. Darf ich dir auch mal ein delete from tablename per Ajax schicken?

      Dürfen sicher, wenn yahoo das dann auch macht, ist es gewollt.

      1. Tach!

        Du machst da ja interessante Dinge. Darf ich dir auch mal ein delete from tablename per Ajax schicken? Dürfen sicher, wenn yahoo das dann auch macht, ist es gewollt.

        Ach, hätte ich da vorher die Ziel-URL lesen sollen? Dann kann ich das vermutlich nicht, weil das serverseitiges Javascript ist. Ansonsten schlüge die SOP zu.

        dedlfix.

        1. Ach, hätte ich da vorher die Ziel-URL lesen sollen? Dann kann ich das vermutlich nicht, weil das serverseitiges Javascript ist. Ansonsten schlüge die SOP zu.

          Vermutlich hat yahoo die CORS-Header gesetzt

          1. hi,

            danke für die ganzen antworten. ich will für meine gratisanwendung auf meiner hans wurst webseite an die gratisdaten von yahoo kommen und hab mir den code oben zusammencopiert und eine passende yql query gesetzt... diese art der programmierung ist mir noch unbekannt, ich guck mir dass morgen nochmal an, ich hoffe ich krieg das irgendwie geregelt ohne das ganze in einer sql datenbank speichern zu müssen oder sowas.... (ich bin eher überzeugter java entwickler, javascript und php können mir egendlich den buckel runterrutschen genauso wie c#, aber bei diesem ding muss ich in die saure apfelsine beißen....)

            1. Tach!

              diese art der programmierung ist mir noch unbekannt, ich guck mir dass morgen nochmal an, ich hoffe ich krieg das irgendwie geregelt ohne das ganze in einer sql datenbank speichern zu müssen oder sowas.... (ich bin eher überzeugter java entwickler, [...]

              Auch mit Java ist es nicht benutzerfreundlich, wenn man blockierend synchron Abfragen startet, die eine Weile dauern. Ich weiß nicht, wie man da asynchron programmiert, aber auch da wird man Callbacks oder ähnliches verwenden. Eine kurze Recherche sagt mir, dass es das von C# oder TypeScript (demnächst) bekannte async-await nicht gibt und damit auch kein Verstecken von asynchronen Abläufen in synchron aussehendem Code.

              Anscheinend hast du aber auch in Java noch keine asynchronen Dinge programmiert, sonst wäre dir sicher dein aktuelles Problem nicht auf die Füße gefallen, weil du Parallelen hättest ziehen können. Wie auch immer, die Promise-Technik ist grad im kommen und in Zukunft werden die Javascript-Engines in den Bowsern auch nativ Promises unterstützen. Bis dahin, bedient man sich externer Bibliotheken, wie beispielsweise Kris Kowals Q.

              Ich hab das vor einer Weile bei einem ähnlichen Problem schon einmal grundlegend zu erklären versucht: siehe dort.

              dedlfix.

        2. Ach, hätte ich da vorher die Ziel-URL lesen sollen? Dann kann ich das vermutlich nicht, weil das serverseitiges Javascript ist. Ansonsten schlüge die SOP zu.

          Warum sollte sie das? Das Beispiel des OP sieht mir nach JSONP aus.

          1. Tach!

            Ach, hätte ich da vorher die Ziel-URL lesen sollen? Dann kann ich das vermutlich nicht, weil das serverseitiges Javascript ist. Ansonsten schlüge die SOP zu.

            Warum sollte sie das?

            Dass das nicht zwangsläufig so sein muss, hat ja unknown schon geschrieben.

            Das Beispiel des OP sieht mir nach JSONP aus.

            Aber das passt nach meinem Dafürhalten irgendwie gar nicht auf das, was die Wikipedia (deutsch und englisch) über JSONP schreibt.

            dedlfix.

            1. Das Beispiel des OP sieht mir nach JSONP aus. Aber das passt nach meinem Dafürhalten irgendwie gar nicht auf das, was die Wikipedia (deutsch und englisch) über JSONP schreibt.

              Du hast völlig Recht. Ich hatte im Hinterkopf, dass jQuery bei getJSON automatisch so agiert. Tut es aber nur unter bestimmten Voraussetzungen, die hier nicht erfüllt sind. Sorry.

              1. Danke für die erklärungen. ich hab jetzt versucht die ganze sache in eine unsichtbare div namens cand zu packen. leider (obwohl ich die daten auf meiner seite sehe wenn ich unsichtbar weg mache) hab ich das gleiche problem wie oben beschrieben.

                function getData() {
                	var result = "";
                
                    var url = "http://query.yahooapis.com/v1/public/yql";
                    var symbol = 'AAPL';//$("#symbol").val();
                	
                	var dateInput1 = '2015-01-01';
                	var dateInput2 = '2015-05-01';
                	var data = encodeURIComponent("select * from yahoo.finance.historicaldata where symbol = 'YHOO' and startDate = '2009-09-11' and endDate = '2010-03-10'");
                   	
                	var cand = document.getElementById("cand");
                    $.getJSON(url, 'q=' + data + "&format=json&diagnostics=true&env=http://datatables.org/alltables.env")
                        .done(function (data) {
                		
                		result = data.query.results.quote[0].Close;
                		for(var i=1; i<data.query.count; i++) {
                			result += "," + data.query.results.quote[i].Close;
                		}
                		
                		cand.innerText = result;
                		console.log("result",cand.innerText);
                    }
                	).fail();
                	result = cand.innerHTML.split(",");
                	var inhalt=document.getElementById('cand').innerText;
                	console.log("result",cand.innerText);
                	return result;
                }
                

                kennt ihr ne beschreibung wie man das mit diesen response löst? ich hab nur irgend welche http sachen dazu gefunden....

                1. Tach!

                  leider (obwohl ich die daten auf meiner seite sehe wenn ich unsichtbar weg mache) hab ich das gleiche problem wie oben beschrieben.

                  Weil du anscheinend immer noch nicht das Prinzip der Asynchronität verinnerlicht hast. Wenn die Funktion $.getJSON() aufgerufen wird, kommt sie sofort zum Aufrufer zurück. Alles was nach ihr steht, kann nicht auf das Ergebnis zugreifen, weil das noch nicht da ist. Wenn das Ergebnis bereitsteht, wird die an done() übergebene Funktion aufgerufen. Nur in ihr kann auf das Ergebnis zugegriffen werden, und außerdem in allem, was von ihr aus aufgerufen wird oder was nach ihrem Aufruf (zeitlich, nicht code-zeilen-örtlich) stattfindet.

                  kennt ihr ne beschreibung wie man das mit diesen response löst? ich hab nur irgend welche http sachen dazu gefunden....

                  Mit $.getJSON().done().fail() verwendest du bereits das Promise-Muster. Wenn du dich mit dem ergebnisverarbeitenden Code auf die done()-Methode beschränken kannst, ist das alles was du tun musst: deinen Code dareinschreiben.

                  Soll hingegen nach dem Aufruf von getData() (und damit außerhalb davon) auf das Ergebnis zugegriffen werden, musst du selbst ein Promise erstellen. Dazu musst du das Promise-Muster generell und nicht nur bezogen auf dein eigentliches Problem kennenlernen.

                  dedlfix.

                  1. kann ich die daten die ich mir in dem getjson hole irgendwo in meine webseite schreiben und dann später außerhalb von getjson darauf zugreifen?

                    1. Tach!

                      kann ich die daten die ich mir in dem getjson hole irgendwo in meine webseite schreiben und dann später außerhalb von getjson darauf zugreifen?

                      Ja, natürlich, aber du kommst dabei nicht um Asynchronität herum. Jeglicher Code, der darauf zugreifen muss, darf erst laufen, wenn die Daten da sind. Das musst du sicherstellen. Und dafür hat man sich Callbacks und das Promise-Muster als Lösungen ausgedacht.

                      dedlfix.

                2. Danke für die erklärungen. ich hab jetzt versucht die ganze sache in eine unsichtbare div namens cand zu packen. leider (obwohl ich die daten auf meiner seite sehe wenn ich unsichtbar weg mache) hab ich das gleiche problem wie oben beschrieben.

                  Weil du es immer noch nicht anders machst. Zu dem Zeitpunkt wo du das split machst ist nichts in deinem unsichtbaren div drinn.
                  Du musst es ins Callback schreiben. Das Callback wird durch einen Eventhandler aufgerufen (oder ist der vielleicht der Eventhandler). Ehe allerdings dieser Eventhandler abgearbeitet wird, muss

                  1. die Response auf deinen Request angekommen sein im Browser
                  2. der vorherige Eventhandler abgearbeitet sein
                    Js hat nur einen Thread (Worker außen vor) in den durch Events ausgelöst immer wieder Codestücke aus einer Queue abgearbeitet werden. Der Browser hängt in diese Queue immer den Eventhandler rein, wenn das entsprechende Event kommt.
                    Du bist jetzt in Abarbeitung mit deinem getData. Die done-Funktion kommt erst irgendwann in die Queue.
  3. Ich lasse den überflüssigen Bloat aus deinem Beispiel Mal weg, und konzentriere mich in meiner Antwort auf das Skelett deiner Anwendung. Die anderen Antwortenden haben dir ja schon erklärt, dass dein Problem auf asynchronen Kontrollfluss zurückzuführen ist, und das Promises ein populäres Entwurfsmuster zu dessen Bewältigung sind. Du nutzt sogar schon ohne es zu wissen Promises, nämlich wenn du die done()-Methode von jQuery benutzt.

    function getData() {
       var url;
       // ...
       $.getJSON(url).done(function(data){
          // (1)
          //tu etwas mit den JSON-Daten
       }).fail(function(){
          //tu etwas anderes im Fehlerfall
       });
       // (2)
    }
    // (3)
    

    An Stelle (1) ist dir ja schon bewusst, wie du auf das JSON-Erbenis zugreifen kannst. An den Stellen (2) und (3) hast du noch Schwierigkeiten. Der Clou dabei ist einfach, das Anfrage-Objekt (das Promise von jQuery) an den beiden nachfolgenden Stellen wieder zu verwenden. Diese Wiederverwendung erreicht man, indem man sich das Anfrage-Objekt in einer Variabeln zwischenspeichert (2) bzw. indem man das Promise von der Funktion zurückgibt (3).

    function getData() {
       var url;
       // ...
       var request = $.getJSON(url);
       request.done(function(data){
          // (1)
          //tu etwas mit den JSON-Daten
       }).fail(function(){
          //tu etwas anderes im Fehlerfall
       });
       // (2)
       request.done(function(data){
          //tu etwas mit den JSON-Daten
       });
       return request;
    }
    // (3)
    getData().done(function(data){
       //tu etwas mit den JSON-Daten
    });
    

    Das spiegelt deinen Problemfall noch nicht exakt wieder, denn man kann auf diese Weise nicht die JSON-Daten an Stelle (1) weiterverarbeiten und an den Stellen (2) und (3) mit dem Zwischenergebnis weiter machen. Zu diesem Zweck, kann man sich ein eigenes Promise basteln (beachte, dass ich mich im Beispiel für native Promises entschieden habe und gegen jQuerys Variante).

    function getData() {
       var url;
       // ...
       var promise = new Promise(function(resolve,reject){
          $.getJSON(url).done(function(data){
             // (1)
             //tu etwas mit den JSON-Daten
             var result = doSomething(data);
             resolve(result); // Hier wird das Zwischenergebnis für die nachfolgenden Schritte bereit gestellt
          }).fail(function(){
             //tu etwas anderes im Fehlerfall
             reject(); // Hier wird der Fehlerfall propagiert
          });
       });
       
       // (2)
       promise.then(function(data){ // .then() ist das Pendant zu jQuerys .done()
          // tu etwas mit dem Zwischenergebnis von Schritt (1)
       });
       return promise;
    }
    // (3)
    getData().then(function(data){
      // tu etwas mit dem Zwischenergebnis von Schritt (1)
    });
    

    (Code ist ungetestet)

    1. Tach!

      (beachte, dass ich mich im Beispiel für native Promises entschieden habe und gegen jQuerys Variante).

      Warum? Weil du den Internet Explorer (selbst den 11er, aber nicht den Edge) ausschließen möchtest? ;)

      dedlfix.

      1. Warum? Weil du den Internet Explorer (selbst den 11er, aber nicht den Edge) ausschließen möchtest? ;)

        Ganz einfach, weil ich die Prmoise/A+-Variante auswendig kenne und bei jQuery noch hätte nachschlagen müssen.