30

我已经为此苦苦挣扎了一段时间。我是 Javascript 的新手,并且一直认为我编写的代码一直在异步运行。这是一个通用示例:

我在函数 a 中运行了一些代码。然后函数 A 调用函数 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);
}

注意: initialize 在我的 html 中被 body onload="initialize()" 调用。

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

附带的问题:我应该如何在外部脚本中拆分我的函数?什么被认为是好的做法?我应该将所有函数都塞进一个外部 .js 文件中,还是应该将类似的函数组合在一起?

4

4 回答 4

39

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

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

}

...
于 2012-11-19T13:57:10.490 回答
15

我……一直觉得我写的代码一直在异步运行。

是的,它确实。您的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();
    }
});

例如,你完全异步。

于 2012-11-19T13:54:22.660 回答
1

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

于 2012-11-19T13:54:39.570 回答
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);
}
于 2012-11-19T13:55:21.317 回答