Кажется, JavaScript не ждет возвращаемых значений

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

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

Чтобы обойти это, я вызову функцию A, вызывающую функцию 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 требует значения широты и долготы, полученные функцией Geocode, но я получаю сообщение об ошибке в консоли, в котором говорится, что результаты не определены. Что здесь происходит? Я пришел с Java, поэтому я немного озадачен тем, как происходит поток данных здесь, в JS! Это будут ценные уроки на будущее!

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

4 ответа

Решение

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

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

}

...

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

Да, это так. Ваш geocode Функция не может вернуть результаты вызова в Google API, потому что функция возвращается до завершения вызова 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();
    }
});

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

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

Обычно, когда функции вызываются в 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);
}

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

Другие вопросы по тегам