2012-11-19 6 views
25

Ich habe schon eine Weile damit zu kämpfen. Ich bin neu in Javascript und habe den Eindruck, dass der Code, den ich geschrieben habe, asynchron ausgeführt wurde. Hier ist ein generisches Beispiel:JavaScript scheint nicht auf Rückgabewerte zu warten

Ich führe einen Code in der Funktion a. Funktion A ruft dann Funktion B auf, die eine Variable an A zurückgeben muss, damit A sie in späteren Operationen verwenden kann. Es scheint jedoch, dass, wenn A B anruft, es immer noch seinen eigenen Code ausführt, nicht wartet, blockiert für seinen Rückgabewert, und B ist nicht schnell genug, so dass A den Punkt erreicht, an dem es die Rückgabe benötigt hätte Wert und ich bekomme einen undefinierten Variablentyp Fehler.

Die Weise, die ich um dieses gearbeitet habe, habe Funktion A Anruf Funktion B, die dann eine Funktion C aufruft, die tun würde, was die späteren Operationen, die A mit dem Rückgabewert tun würde .... Ich bin Art der Serialisierung obwohl statt kehrt Anrufe meinen Code durch ... das ist umständlich ...

Hier ist ein Beispiel, wenn es passiert in eigentlichen Code:

function initialize() { 
    //Geocode Address to obtin Lat and Long coordinates for the starting point of our map 
    geocoder = new google.maps.Geocoder(); 
    var results = geocode(geocoder); 
    makeMap(results[0].geometry.location.lat(), results[0].geometry.location.lng()); 

} 

function geocode(geocoder) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      return results; 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 

} 

function makeMap(lat, long) { 
    // alert(lat); for debuging 
    var mapOptions = { 
     center: new google.maps.LatLng(lat, long), 
     zoom: 17, 
     mapTypeId: google.maps.MapTypeId.ROADMAP 
    }; 
    map = new google.maps.Map(document.getElementById("map_canvas"), 
     mapOptions); 
} 

Hinweis: initialize von Körper onload aufgerufen wird = "initialisiere()" in meinem HTML.

Also das Problem ist, dass die makeMap die Längen-und Breitengrad Werte von der Geocode-Funktion erhalten, aber ich bekomme einen Fehler in der Konsole sagt Ergebnisse ist undefiniert. Was ist los? Ich kam aus Java, daher bin ich etwas verwirrt darüber, wie der Datenfluss hier in JS abläuft! Dies wird wertvolle Lehren für die Zukunft sein!

Auf einer Seite Frage: Wie soll ich meine Funktionen über externe Skripte teilen? Was wird als gute Praxis angesehen? sollten alle meine Funktionen in eine externe .js-Datei gepackt werden oder sollte ich ähnliche Funktionen gruppieren?

+3

In Ordnung, danke, ich bin neu hier! –

Antwort

31

Sie scheinen ein gutes Verständnis des Problems zu haben, aber es klingt, als ob Sie nicht vertraut sind mit der Art, es zu lösen. Die häufigste Möglichkeit, dies zu beheben, ist die Verwendung eines Rückrufs. Dies ist im Grunde die asynchrone Methode, auf einen Rückgabewert zu warten. Hier ist, wie Sie es in Ihrem Fall verwenden könnten:

function initialize() { 
    //Geocode Address to obtin Lat and Long coordinates for the starting point of our map 
    geocoder = new google.maps.Geocoder(); 
    geocode(geocoder, function(results) { 
     // This function gets called by the geocode function on success 
     makeMap(results[0].geometry.location.lat(), results[0].geometry.location.lng());   
    }); 
} 

function geocode(geocoder, callback) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // Call the callback function instead of returning 
      callback(results); 
     } else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 

} 

... 
11

Ich ... hatte den Eindruck, dass der Code, den ich geschrieben habe, asynchron lief.

Ja, tut es. Ihre geocode Funktion kann nicht die Ergebnisse des Aufrufs an die Google API zurückgeben, da die Funktion vor dem Google-Aufruf beendet wird. Siehe Anmerkung unten:

function geocode(geocoder) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // +---------- This doesn't return anything from your 
      // v   geocode function, it returns a value from the callback 
      return results; 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
     } 
    }); 
} 

Stattdessen müssen Sie Ihre geocode Funktion Code, so dass es einen Rückruf akzeptiert, die sie anrufen, wenn sie die Ergebnisse hat. Z.B .:

// Added a callback arg ---v 
function geocode(geocoder, callback) { 
    //do geocoding here... 

    var address = "3630 University Street, Montreal, QC, Canada"; 
    geocoder.geocode({ 'address': address }, function (results, status) { 
     if (status == google.maps.GeocoderStatus.OK) { 
      // v---------- Call the callback 
      callback(results); 
      } 
     else { 
      alert("Geocode was not successful for the following reason: " + status); 
      callback(null); // <--- Call the callback with a flag value 
          // saying there was an error 
     } 
    }); 
} 

Dann anstatt es wie folgt aus:

var results = geocode(someArgHere); 
if (results) { 
    doSomething(results); 
} 
else { 
    doSomethingElse(); 
} 

Sie nennen es wie folgt aus:

geocode(someArgHere, function() { 
    if (results) { 
     doSomething(results); 
    } 
    else { 
     doSomethingElse(); 
    } 
}); 

Z. B. Sie gehen voll asynchron.

+0

ist das nicht, was die anonyme Funktion (die zwei Parameter Ergebnisse, Status nimmt) tut? –

+0

@GeorgesKrinker: Ja, Sie können "Callback" direkt an Google weitergeben, wenn Sie das Ergebnis nicht transformieren müssen, bevor Sie es an den aufrufenden Code zurücksenden. –

1

Die Rückgabeanweisung in der anonymen Funktion wird von der anonymen Funktion zurückgegeben, nicht von der äußeren Geocode-Funktion. Die Geocode-Funktion gibt undefined zurück. Die Methode "geocoder.geocode" kann die anonyme Funktion jederzeit aufrufen, synchronisieren oder asynchron ausführen. Überprüfen Sie die Dokumente dafür.

1

In der Tat haben Sie richtig erkannt, dass die Aufrufe asynchron sind, und Sie erhalten keinen richtigen Rückgabewert.

Normalerweise, wenn Funktionen in js aufgerufen werden, sind sie synchron.

e.g. a() calls b(), and a() waits until b() to finish before continuing. 

jedoch in bestimmten Situationen, wie Ajax oder JSONP Telefonieren wird asynchron ausgeführt. Genau das passiert, wenn Sie anrufen.

Ihre Ausführung:

initialize() is called; 
initialize() calls geocoder(); 
geocoder makes a request to Google, and returns null in the meantime. 
initialze() calls makemap() 
the Google geocoder returns at some point, and executed the success callback, which you have defined as "return results;", but there is nothing to return, since the function has already ended. 

So, speziell den Rückruf verwenden, die bereits in den Geocoder Ruf aufgebaut wird:

if (status == google.maps.GeocoderStatus.OK) { 
    makeMap(results); 
} 
+0

Dies scheint auch zu passieren, wenn ich anstelle eines Anrufs zu einem externen Dienst (wie der oben genannte async google Aufruf), ich ein Array durchlaufen und das Array zurückgegeben, weil ich es brauchte ... warum ist das? –

+0

Das sollte synchron sein. Kannst du das in einem [jsFiddle] (http://jsfiddle.net) reproduzieren? –

+0

Ok Ich bin nicht sicher, wie man das benutzt, aber http://jsfiddle.net/gplyh/1/ Der Ort, an dem das Problem auftritt, ist, wenn loadData() addMarkers() aufruft, nachdem versucht wurde, die Daten von getStations() wiederherzustellen sollte ein Array mit den Stationen zurückgegeben haben ... –