w3schools tag tab style page for color javascript asynchronous callback asynccallback

javascript - tag - title of page html



JavaScript no parece esperar los valores devueltos (4)

He estado luchando con esto por un tiempo ahora. Soy nuevo en Javascript, y he tenido la impresión de que el código que he estado escribiendo se ha estado ejecutando de forma asíncrona. Aquí hay un ejemplo genérico:

Ejecuto un código en la función a. La función A luego llama a la función B, que necesita devolver una variable a A para que A pueda usarla en sus operaciones posteriores. Sin embargo, parece que cuando A llama a B, sigue ejecutando su propio código, no esperando bloqueado por su valor de retorno, y B no es lo suficientemente rápido como para que A termine llegando al punto donde habría necesitado utilizar el retorno. valor y obtengo un error de tipo de variable no definido .

La forma en que he trabajado alrededor de esto es hacer que la función A llame a la Función B que luego llama a una Función C que haría lo que harían las operaciones posteriores que A haría con el valor de retorno ... Estoy serializando mi código a través de llamadas en lugar de retornos ... eso es engorroso ...

Aquí hay un ejemplo de cuando sucede en el código real:

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); }

Nota: initialize es llamado por body onload = "initialize ()" en mi html.

Entonces, el problema es que makeMap requiere los valores de latitud y longitud obtenidos por la función Geocode, pero aparece un error en la consola que dice que los resultados no están definidos. Que esta pasando? Vengo de Java, así que estoy un poco confundido acerca de cómo el flujo de datos está sucediendo aquí en JS. ¡Estas serán valiosas lecciones para el futuro!

En una pregunta secundaria: ¿cómo debería dividir mis funciones en scripts externos? ¿Qué se considera una buena práctica? ¿Deberían todas mis funciones estar agrupadas en un archivo .js externo o debería agrupar funciones similares?


Yo ... he tenido la impresión de que el código que he estado escribiendo se ha estado ejecutando de forma asíncrona.

Sí, lo hace. Su función de geocode no puede devolver los resultados de la llamada a la API de Google, porque la función regresa antes de que finalice la llamada de Google. Vea la nota abajo:

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); } }); }

En su lugar, debe codificar su función de geocode para que acepte una devolución de llamada a la que llamará cuando tenga los resultados. P.ej:

// 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 } }); }

Entonces, en lugar de usarlo así:

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

Lo llamas así:

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

Por ejemplo, vas completamente asincrónico.


De hecho, tiene razón al darse cuenta de que las llamadas son asincrónicas y no está obteniendo un valor de retorno adecuado.

Normalmente, cuando las funciones se llaman en js, son sincrónicas.

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

Sin embargo, en determinadas situaciones, como hacer llamadas ajax o jsonp, se realiza de forma asincrónica. Esto es precisamente lo que sucede cuando llamas a geocode() .

Tu ejecución:

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.

Así que, específicamente, utilice la devolución de llamada que ya está incorporada en la llamada al geocodificador:

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


La declaración de devolución dentro de la función anónima vuelve de la función anónima, no de la función de geocodificación externa. La función de geocodificación devuelve indefinido. El método geocoder.geocode puede llamar a la función anónima cuando lo desee, sincronizar o sincronizar. Verifique los documentos para ello.


Parece que comprende bien el problema, pero parece que no está familiarizado con la forma de resolverlo. La forma más común de abordar esto es mediante el uso de una devolución de llamada. Esta es básicamente la forma asíncrona de esperar un valor de retorno. Así es como podría usarlo en su caso:

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); } }); } ...