Подтвердить что ты не робот

JavaScript, похоже, не ждет возвратных значений

Я боролся с этим какое-то время. Я новичок в Javascript, и у меня создалось впечатление, что код, который я писал, работает асинхронно. Вот общий пример:

Я запускаю некоторый код в функции a. Затем функция A вызывает функцию B, которая должна вернуть переменную в A, чтобы A могла использовать ее в своих более поздних операциях. Кажется, что когда A называет B, он по-прежнему продолжает запускать свой собственный код, не ожидая, чтобы заблокировал его возвращаемое значение, а B не достаточно быстро, так что A заканчивается тем, что ему нужно было бы использовать возврат значение, и я получаю undefined ошибку типа переменной.

Как я работал над этим, есть функция. Вызов Функция B, которая затем вызывает функцию C, которая будет делать то, что последующие операции, которые A будет делать с возвращаемым значением.... Я вроде как сериализую свою код через вызовы вместо возвратов... это громоздко, хотя...

Вот пример того, когда это происходит в реальном коде:

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

Примечание: инициализация вызывается телом onload = "initialize()" в моем html.

Итак, проблема заключается в том, что makeMap требует значений lat и долготы, полученных функцией Geocode, но я получаю сообщение об ошибке в консоли, говорящей, что результаты undefined. Что происходит? Я пришел из Java, поэтому я немного смущен тем, как поток данных происходит здесь, в JS! Это будет ценным уроком для будущего!

По вопросу о стороне:. Как мне разделить мои функции на внешние скрипты? Что считается хорошей практикой? Должны ли все мои функции быть переполнены в один внешний файл .js или я должен группировать подобные функции вместе?

4b9b3361

Ответ 1

У вас, похоже, хорошее понимание проблемы, но похоже, что вы не знакомы с тем, как ее решить. Наиболее распространенным способом решения этого является использование обратного вызова. Это в основном асинхронный способ ожидания возвращаемого значения. Вот как вы могли бы использовать его в своем случае:

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

}

...

Ответ 2

У меня... создалось впечатление, что код, который я писал, работает асинхронно.

Да, да. Функция geocode не может вернуть результаты вызова в API Google, поскольку функция возвращается до завершения вызова Google. См. Примечание ниже:

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

Вместо этого вы должны закодировать свою функцию geocode так, чтобы она принимала обратный вызов, который он вызывается, когда он имеет результаты. Например:.

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

Затем вместо использования этого:

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

Вы называете это следующим образом:

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

Например, вы полностью асинхронны.

Ответ 3

Оператор return внутри анонимной функции возвращается из анонимной функции, а не из внешней геокодовой функции. Функция геокодирования возвращает undefined. Метод geocoder.geocode может вызывать анонимную функцию, когда захочет, синхронизировать или асинхронно. Проверьте документы для него.

Ответ 4

Действительно, вы правильно понимаете, что вызовы асинхронны, и вы не получаете правильное возвращаемое значение.

Обычно, когда функции вызывают в js, они синхронны.

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

Однако в некоторых ситуациях, например, при вызове ajax или jsonp, он выполняется асинхронно. Это именно то, что происходит, когда вы вызываете geocode().

Ваше выполнение:

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.

Итак, в частности, используйте обратный вызов, который уже встроен в вызов геокодера:

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