FPcond
FPcond

Reputation: 591

possible nested asynchronous function

So I have read a lot of the different answers about asynchronous functions on here but I think I am over thinking my problem, or I have been staring at it just to long and I cant figure it out. So your help is greatly appreciated.

So I am parsing a csv file and then trying to get the lat/long through another api. but I cant access the lat/lng outside of the function. below is my code I have commented it to the best of my ability please let me know if there are any questions or a much better way to do this.

Thanks,

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

Upvotes: 2

Views: 66

Answers (1)

Fabr&#237;cio Matt&#233;
Fabr&#237;cio Matt&#233;

Reputation: 70139

Your code tries to synchronously consume asynchronous data -- you're synchronously trying to access the results (location) before any of the asynchronous operations have finished.

As you have multiple async operations running in parallel, you can make use of async.parallel to aid in controlling the asynchronous flow:

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

Also, for loops don't create a scope, so I've swapped it by a forEach in order to scope the info and passLoc variables inside each iteration.

Here's a slightly more condensed version using ES5's 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);
        }
    });
}

Upvotes: 3

Related Questions