【问题标题】:async errors with function call inside nested async loop嵌套异步循环中函数调用的异步错误
【发布时间】:2018-04-02 22:03:40
【问题描述】:

我正在制作一个坑洞地图,并且正在从服务器加载坑洞数据并将其作为标记放在地图上的阶段。由于数据检索和我的应用程序所依赖的 API(道路、地理位置等)是异步的,因此我的代码最终被重构为异步运行。我将向地图添加标记的代码块重构为:

/* put all potholes on the map 
 * Parameters:
 *  • callback : the function to call next
 */
function addPotholeMarkers(callback)
{
    var DEBUG = false;
    // guarantee that callback is function
    if ((callback) && (typeof(callback) !== 'function')) throw new TypeError('callback is something, but not a function. Thrown from addPotholeMarkers().');
    // add all the markers for them to the map
    async.waterfall([
        function(cb) { 
            async.eachOf(potholeAddresses, function(value, key) {
                async.eachOf(value, function (v, k) { 
                    addPotholeMarker(v, false);
                })
            })
            if (cb && typeof cb === 'function') cb(null);
        }, function(cb) {
            async.eachOf(potholeCoordinates, function(value, key) { 
                async.eachOf(value, function(v, k) { 
                    async.setImmediate(function() { addPotholeMarker(v); }); // This came from 
                })
            })
        }], function(err, results) {
            console.log('trying to center map');
            reCenterMap();
            console.log('Map recentered');
            if (callback) { 
                callback(err);
            }
        });


}

addPotholeMarker() 看起来像:

/* code was initially obtained from https://developers.google.com/maps/documentation/roads/inspector */
/* Adds marker to map.
 * Parameters : 
 *  • potholeData  : a PotholeData (or PotholeDataFromCoords) object
 *  • snappedToRoad: boolean
 * Returns : 
 *  • the marker that was added to the map, or null if arguments invalid
 */
function addPotholeMarker(potholeData, snappedToRoad) {
  // make sure potholeState is either falsy or contains iconURL string
  if ((!potholeData.potholeState) || ((potholeData.potholeState) && (potholeData.potholeState.iconURL === undefined))) throw new Error('invalid potholeData');
  // let's make sure to snap this to road if it isn't already...  
  var coords = new GPSCoordinates(potholeData.lat, potholeData.lng);
  if (!snappedToRoad) 
  { 
    var potholeMarker = 'a garbage return value';
    getRoadCoordinates(coords).done(function(response) {
        var coords = response.snappedPoints[0].location;
        potholeData.lat = coords.latitude;
        potholeData.lng = coords.longitude;
        return (potholeMarker = addPotholeMarker(potholeData, true));
   /*     potholeMarker = addPotholeMarker(potholeData, true);
        return potholeMarker;*/
    });
    return; 
    //return potholeMarker;
  }
  var marker = new google.maps.Marker({
    position: coords,
    title: coords.toString(),
    map: map,
    opacity: 0.5,
    icon: ((potholeData.potholeState.iconURL !== undefined) ? potholeData.potholeState.iconURL : PURPLE_MARKER)
  });

  // make marker have effect when mouseout,mouseover
  marker.addListener('mouseover', function(mouseEvent) {
    marker.setOpacity(1.0);
  });
  marker.addListener('mouseout', function(mouseEvent) {
    marker.setOpacity(0.5);

  });

  var infoWindow = createInfoWindow(potholeData);
  // save infoWindow for later reference
  infoWindows[statesMap.get(getPotholeStateFor(potholeData.potholeState))].push(infoWindow);
  // on click of marker, show infoWindow
  marker.addListener('click', function(mouseEvent) { 
    infoWindow.open(map, marker);
  });
  // add this to potholeMarkers
  potholeMarkers[statesMap.get(getPotholeStateFor(potholeData.potholeState))].push(marker);  
  return marker;
}

This app is hosted on Google Apps Script(您需要 Google 帐户来运行它),并使用客户端 async library

此代码在成功运行时应该将地图重新​​居中在所有标记的位置平均值处。 reCenterMap() 可以正常工作,所以我在尝试 MVCE 时省略了它。

当我运行代码时

在异步循环的任何滴答声中,potholeCoordinates 对象(即Object<Array<PotholeData> >)的成员显示为空。如何解决这个问题?

【问题讨论】:

  • 为什么这被否决了?

标签: javascript google-maps asynchronous async.js


【解决方案1】:

在向朋友展示了这个问题,考虑了他对此的建议,并昨晚熬夜自己解决了几个小时后,我做了以下更改:

  1. 与此问题无关,但

我将索引statesMap.get(getPotholeStateFor(potholeData.potholeState)) 更改为statesMap.get(getPotholeStateFor(potholeData))。结果是我给getPotholeStateFor() 提供了错误的对象,导致它返回了错误的状态。

  1. 我把addPotholeMarker(potholeData, snappedToRoad)的签名改成了addPotholeMarker(potholeData, snappedToRoad, callback); /* 因为显然我忘记了,对于异步函数,回调必须传递给最低级别的函数并在那里调用,签名为callback(err, results) 或类似的东西 */
  2. addPotholeMarker() 内部,我确保使用callback,但以模块化方式:

    if (callback) return callback(null, potholeMarker); return potholeMarker; /* 我将此更改应用于if (!snappedToRoad),但该 if 语句仍然损坏:它将在完成附加到数组的任务之前返回,return callback(null, potholeMarker) 将导致回调被调用两次。我最终可能不得不重构整个函数,特别是附加到potholeMarkers(顺便说一句,它是全局的)这个函数之后(它将是这个函数的回调)*/

  3. addPotholeMarkers() 中最里面的异步循环从:

    async.eachOf(value, function(v, k) { async.setImmediate(function() { addPotholeMarker(v); }); })

async.eachSeries(value, function(pothole, fn) { addPotholeMarker(pothole, true, //pothole.isSnappedToRoad(), fn); })

注意 pothole.isSnappedToRoad() 被注释掉,因为它返回 false,并且 addPotholeMarker() 在第二个参数 false 的主调用堆栈中无法正常工作。另外,它应该返回true,但这不是因为这种类型的错误发生在addPotholeMarkers()之前的函数中; /* 接下来我要解决这个问题! */

【讨论】:

    猜你喜欢
    • 2020-03-07
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 2015-06-05
    • 2012-10-12
    • 1970-01-01
    • 2016-08-18
    相关资源
    最近更新 更多