【问题标题】:JavaScript doesn't seem to wait for return valuesJavaScript 似乎不等待返回值
【发布时间】:2012-11-07 10:53:11
【问题描述】:

我已经为此苦苦挣扎了一段时间。我是 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);
}

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

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

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

【问题讨论】:

  • 好的,谢谢,我是新来的!

标签: javascript asynchronous callback asynccallback


【解决方案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】:

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

    一般情况下,在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);
    }
    

    【讨论】:

    • 如果我不是调用外部服务(如上面的异步 google 调用),而是遍历一个数组并返回该数组,因为我需要它,这似乎也会发生......为什么会这样?
    • 应该是同步的。你能在jsFiddle 中重现这个吗?
    • 好吧,我不知道如何使用它,但是jsfiddle.net/gpLYH/1 问题发生的地方是当 loadData() 在尝试从 getStations() 中检索应该有的数据后调用 addMarkers()返回一个包含电台的数组...
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

      例如,您完全异步进行。

      【讨论】:

      • 这不是匿名函数(接受两个参数结果,状态)在做什么吗?
      • @GeorgesKrinker:是的,如果您在将结果返回给调用代码之前不需要转换结果,您可以将callback 直接传递给 Google。
      猜你喜欢
      • 2018-10-09
      • 1970-01-01
      • 2020-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-16
      • 1970-01-01
      相关资源
      最近更新 更多