JavaScript似乎不等待返回值

时间:2012-11-19 13:47:54

标签: javascript asynchronous callback asynccallback

我一直在努力解决这个问题。我是Javascript的新手,并且一直认为我写的代码已经异步运行了。这是一个通用的例子:

我在函数a中运行了一些代码。函数A然后调用函数B,函数B需要将变量返回给A,因此A可以在以后的操作中使用它。看来,当A调用B时,它仍然继续运行自己的代码,而不是等待阻止其返回值,并且B不够快,使得A最终到达需要使用返回的点我得到一个未定义的变量类型错误

我解决这个问题的方法是使用函数A调用函数B然后调用函数C,该函数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);
}

注意:初始化由我的html中的body onload =“initialize()”调用。

所以问题是makeMap需要Geocode函数获得的纬度和经度值,但是在控制台中我得到一个错误,说结果是未定义的。到底是怎么回事?我来自Java,所以我对JS中数据流的发生方式感到有些困惑!这将是未来的宝贵经验!

关于一个侧面问题:我应该如何在外部脚本之间拆分我的功能?什么是好习惯?我的所有功能都应该塞进一个外部.js文件中,还是应该将功能组合在一起?

4 个答案:

答案 0 :(得分:35)

您似乎对问题有了很好的理解,但听起来您并不熟悉解决问题的方法。解决此问题的最常见方法是使用回调。这基本上是等待返回值的异步方式。以下是您在案例中如何使用它:

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

}

...

答案 1 :(得分:12)

  

我...的印象是我写的代码一直在异步运行。

是的,确实如此。您的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函数进行编码,以便它接受一个回调,当它有结果时它将调用它。 E.g:

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

例如,你完全异步。

答案 2 :(得分:1)

匿名函数内的return语句从匿名函数返回,而不是从外部地理编码函数返回。地理编码函数返回undefined。 geocoder.geocode方法可以在需要,同步或异步时调用匿名函数。检查文档。

答案 3 :(得分:1)

实际上,你是正确的认识到调用是异步的,并且你没有得到正确的返回值。

通常,在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);
}