【问题标题】:Need help creating async function with callback需要帮助创建带有回调的异步函数
【发布时间】:2015-07-23 16:12:37
【问题描述】:

我有一个 JSON 对象,其中包含一些条目(约会),每个条目都有一个“位置 ID”。然后我循环遍历这些条目并通过 socketIO 向我的 nodeJS 服务器发出请求,以从具有位置 id 的文档中获取数据。

最后我需要一个包含 lat/lng 数据的数组来在地图上创建一些标记。

代码如下:

//controller for showing map
.controller('MapCtrl', function($scope, socket){

    socket.emit('getApp', staticUserid);

        socket.on('getApps', function (appdata) {
                var locArr = [];
                for (var i = 0; i < appdata.length; i++) {
                    if (appdata[i].locationid != '') {
                        locArr.push(appdata[i].locationid);
                    }
                }
                var LatLngArr = [];
                for (var j = 0; j < locArr.length; j++) {
                    socket.emit('getLocation', locArr[j]);
                    socket.on('getLoc', function (locData) {
                        console.log('received lat/lng: ' + locData.lat + '/' + locData.lng);
                        if (!LatLngArr[j]) LatLngArr[j] = []
                        LatLngArr[j][0] = locData.lat;
                        LatLngArr[j][1] = locData.lng;
                    });
                }
                //console.log('test:'+LatLngArr[0][0]);
        });

    var newMarkers = [[52.549678, 13.3879516],[52.5442992, 13.352809],[52.5186283,13.3761181]]; // this should be the generated array
    var newCenter = [52.549678, 13.3879516];
    createMap(newCenter,newMarkers);

})

问题是,var LatLngArr 没有定义在...

socket.on('getLoc', function (locData)

如果有人可以帮助我,那就太好了:-)

非常感谢!

【问题讨论】:

  • 异步,异步,异步。当您了解您只能在传递它的回调中使用异步结果(而不是在该回调之外),那么您将了解您必须做什么来修复您的代码。仅供参考,这是一个常见问题,因为我每天在这里多次看到这样的问题。这是关于该主题的更规范的答案之一:stackoverflow.com/questions/14220321/…

标签: javascript angularjs node.js socket.io


【解决方案1】:

如果你可以使用 Promises

.controller('MapCtrl', function($scope, socket){

    socket.emit('getApp', staticUserid);

    socket.on('getApps', function (appdata) {
        var locArr = [];
        for (var i = 0; i < appdata.length; i++) {
            if (appdata[i].locationid != '') {
                locArr.push(appdata[i].locationid);
            }
        }
        var LatLngArr = [];
        var promises = [];
        for (var j = 0; j < locArr.length; j++) {
            promises[j] = (function(captured_j) {
                return new Promise(function(resolve, reject) {
                    socket.emit('getLocation', locArr[captured_j]);
                    socket.on('getLoc', function (locData) {
                        console.log('received lat/lng: ' + locData.lat + '/' + locData.lng);
                        if (!LatLngArr[captured_j]) LatLngArr[captured_j] = []
                        LatLngArr[captured_j][0] = locData.lat;
                        LatLngArr[captured_j][1] = locData.lng;
                        resolve({index: captured_j, result: LatLngArr[captured_j]});
                    });
                });
            }(j));
        }
        Promise.all(promises).then(function(arr) {
            // ******************************************
            // ******************************************
            // arr is an array of {index: #, result [lat, lng]} - but you can also use LatLngArr
            // ******************************************
            // ******************************************
        });
    });
    var newMarkers = [[52.549678, 13.3879516],[52.5442992, 13.352809],[52.5186283,13.3761181]]; // this should be the generated array
    var newCenter = [52.549678, 13.3879516];
    createMap(newCenter,newMarkers);
})

【讨论】:

    【解决方案2】:

    试试这个

    for (var j = 0; j < locArr.length; j++) {
        (function(captured_j) {
            socket.emit('getLocation', locArr[captured_j]);
            socket.on('getLoc', function (locData) {
                console.log('received lat/lng: ' + locData.lat + '/' + locData.lng);
                if (!LatLngArr[captured_j]) LatLngArr[captured_j] = []
                LatLngArr[captured_j][0] = locData.lat;
                LatLngArr[captured_j][1] = locData.lng;
                //
                // the required result
                //
                if (j === 0) {
                    console.log('test:'+LatLngArr[0][0]);
                }
                //
            });
        }(j));
    }
    

    【讨论】:

    • 显然我对此很生气......复制和粘贴不起作用:-(你能告诉我如何使用你的sn-p吗?非常感谢!
    • 是的,我这样做了,没有错误。但是这样我就不能在 console.log('test').... 行访问数组 LatLngArr 并得到这个错误: TypeError: Cannot read property '0' of undefined
    • 我想你没有理解我的问题...我需要在 socket.on 函数之外的这个数组,因为这个数组循环了好几次。并且 createMap 函数应该只用准备好的填充数组调用一次。你知道我的意思?如果在此处定义了 arr,则该日志只是一个测试示例。
    【解决方案3】:

    您的代码失败的原因是因为该行的异步性质:

    socket.on('getLoc', function (locData)
    

    所以LatLngArr 实际上是在您注册回调时定义的,但在调用回调时不再是。原因是 JavaScript 使用了Closures

    你可以看看这个帖子:How do JavaScript closures work?

    因此,您必须将其包装在 IIFE 中。这样,函数会立即执行,以便 j 的值按照您的预期运行,因为它不再引用外部作用域的 j

    编辑:可以在 David Hermann 的“Effective Javascript”的第 13 项中找到一个非常好的解释——我完全可以推荐这本书。如果您在理解它时遇到问题,请查看此thread 可能会有所帮助。

    【讨论】:

      猜你喜欢
      • 2019-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-08
      • 2013-12-08
      • 2013-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多