JavaScript 似乎不会等待返回值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13455134/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 12:08:49  来源:igfitidea点击:

JavaScript doesn't seem to wait for return values

javascriptasynchronouscallbackasynccallback

提问by Georges Krinker

I've been struggling with this for a while now. I'm new to Javascript, and have been under the impression that the code I've been writing has been running asynchronously. Here is a generic example:

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

I run some code in function a. Function A then calls Function B, who needs to return a variable to A so A can use it in its later operations. It seems though that when A calls B, it still continues to run its own code, not waiting blocked for its return value, and B isn't fast enough such that A ends up reaching the point where it would have needed to use the return value and I get an undefined variable type error.

我在函数 a 中运行了一些代码。函数 A 然后调用函数 B,后者需要将一个变量返回给 A,以便 A 可以在以后的操作中使用它。似乎当 A 调用 B 时,它仍然继续运行自己的代码,而不是等待它的返回值被阻塞,而且 B 的速度不够快,以至于 A 最终达到了它需要使用返回值的程度值,我得到一个未定义的变量类型错误

The way I have worked around this is have function A call Function B which then calls a Function C that would do what the later operations that A would be doing with the return value....I'm kind of serializing my code through calls instead of returns...that is cumbersome though...

我解决这个问题的方法是让函数 A 调用函数 B,然后调用函数 C,该函数将执行 A 将使用返回值执行的后续操作......我有点通过调用序列化我的代码而不是回报......虽然这很麻烦......

Here is an example of when it happens in actual code:

这是在实际代码中发生时的示例:

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

Note:initialize gets called by body onload="initialize()" in my html.

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

So the issue is that the makeMap requires the lat and longitude values obtained by the Geocode function, but I get an error in the console saying results is undefined. What is going on? I came from Java so I'm a little confused about how data flow is happening here in JS! This will be valuable lessons for the future!

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

On a side question:How should I split my functions across external scripts? What is considered good practice? should all my functions be crammed into one external .js file or should I group like functions together?

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

回答by jncraton

You seem to have a good understanding of the problem, but it sounds like you aren't familiar with the way to solve it. The most common way to address this is by using a callback. This is basically the async way to wait for a return value. Here's how you could use it in your case:

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

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

}

...

回答by T.J. Crowder

I...have been under the impression that the code I've been writing has been running asynchronously.

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

Yes, it does. Your geocodefunction cannotreturn the results of the call to the Google API, because the function returns before the Google call completes. See note below:

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

Instead, you must code your geocodefunction so that it accepts a callback which it will call when it has the results. E.g.:

相反,您必须对您的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
        }
   });
}

Then, instead of using it like this:

然后,而不是像这样使用它:

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

You call it like this:

你这样称呼它:

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

E.g., you go fullyasynchronous.

例如,您完全异步。

回答by Douglas

The return statement inside the anonymous function returns from the anonymous function, not from the outer geocode function. The geocode function returns undefined. The geocoder.geocode method may call the anonymous function whenever it wants, sync or async. Check the docs for it.

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

回答by Julian H. Lam

Indeed, you are correct in realizing that the calls are asynchronous, and you are not getting a proper return value.

确实,您正确地意识到调用是异步的,并且您没有获得正确的返回值。

Normally, when functions are called in js, they are synchronous.

通常,当在 js 中调用函数时,它们是同步的。

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

However, in certain situations, such as making ajax or jsonp calls, it is done asynchronously. This is precisely what is happening when you call geocode().

但是,在某些情况下,例如进行 ajax 或 jsonp 调用,它是异步完成的。这正是您调用 时发生的情况geocode()

Your execution:

你的执行:

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.

So, specifically, utilise the callback that is already built into the geocoder call:

因此,特别是利用已经内置到地理编码器调用中的回调:

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