可能的嵌套异步函数

possible nested asynchronous function

所以我在这里阅读了很多关于异步函数的不同答案,但我想我想多了我的问题,或者我一直盯着它看太久了,但我想不通。因此,非常感谢您的帮助。

所以我正在解析一个 csv 文件,然后尝试通过另一个 api 获取 lat/long。但我无法访问函数之外的 lat/lng 。下面是我的代码,我已尽我所能对其进行评论,如果有任何问题或更好的方法,请告诉我。

谢谢,

var location = [] 
function run() {
    http.get(url, function(res) {
        if(res.statusCode === 200) {
            res.pipe(parse(function(err, data) {
                for(i = 1; i < data.length; i++) {
                    var info = data[i];
                    passLoc = info[6].replace('block ', '')
                    passLoc = passLoc.replace(/ /g, "+")
                    getLoc(passLoc, function(loc) {
                        location.push(loc);
                        //If I console.log(location) here I get all the info I want but.....it is printed 100 times becuase it is printed for each i in data.length
                    })
                }
                console.log(location) // loging this here gives me an empty array           
            }))
        }else {
            console.error('The address is unavailable. (%d)', res.statusCode);
        }
    })
}
function getLoc(x, callback) {
    var url = "http://geodata.alleghenycounty.us/arcgis/rest/services/Geocoders/EAMS_Composite_Loc/GeocodeServer/findAddressCandidates?Street=" + x + "&City=Pittsburgh&State=PA&ZIP=&SingleLine=&outFields=&outSR=4326&searchExtent=&f=pjson";
    http.get(url, function(res) {
        var data = '';
        res.on('data', function(chunk) {
            data += chunk;
        });
        res.on('end', function() {
            var d = JSON.parse(data);
            var obj = d.candidates;
            if(obj != '') {
                var loc = obj[0].location
                    var lat = loc.x
                    var lng = loc.y
                    var location = [lat, lng];
                callback(location)
            } else {
                callback(x);
            }
        });
        res.on('error', function(err) {
            callback("error!")
        });
    });
}

您的代码尝试 synchronously consume asynchronous data -- 您正在同步尝试在任何异步操作完成之前访问结果 (location)。

由于您有多个并行的异步操作 运行,您可以使用 async.parallel 来帮助控制异步流程:

var async = require('async');

function run() {
    http.get(url, function(res) {
        if(res.statusCode === 200) {
            res.pipe(parse(function(err, data) {
                // array of async tasks to execute
                var tasks = [];

                data.slice(1).forEach(function(info) {
                    var passLoc = info[6].replace('block ', '').replace(/ /g, '+');

                    // push an async operation to the `tasks` array
                    tasks.push(function(cb) {
                        getLoc(passLoc, function(loc) {
                            cb(null, loc);
                        });
                    });
                });

                // run all async tasks in parallel
                async.parallel(tasks, function(err, locations) {
                    // consume data when all async tasks are finished
                    console.log(locations);
                });
            }));
        }else {
            console.error('The address is unavailable. (%d)', res.statusCode);
        }
    });
}

此外,for 循环不创建作用域,因此我将其替换为 forEach 以限定 infopassLoc 变量的作用域在每次迭代中。

这是一个使用 ES5 的 Array#map 的更简洁的版本:

var async = require('async');

function run() {
    http.get(url, function(res) {
        if(res.statusCode === 200) {
            res.pipe(parse(function(err, data) {
                async.parallel(
                    // map data items to async tasks
                    data.slice(1).map(function(info) {
                        return function(cb) {
                            var passLoc = info[6].replace('block ', '').replace(/ /g, '+');
                            getLoc(passLoc, function(loc) {
                                cb(null, loc);
                            });
                        };
                    }),
                    function(err, locations) {
                        // consume data when all async tasks are finished
                        console.log(locations);
                    }
                );
            }));
        } else {
            console.error('The address is unavailable. (%d)', res.statusCode);
        }
    });
}