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!")
});
});
}
-
Please show only the relevant piece of codethefourtheye– thefourtheye2015年03月01日 14:21:10 +00:00Commented Mar 1, 2015 at 14:21
-
took out the one function that isnt used hopefully that helpsFPcond– FPcond2015年03月01日 14:23:15 +00:00Commented Mar 1, 2015 at 14:23
-
Not just that. Most of your application logic and the comments related to that, may not be related to the actual problem. Please remove them also. Because, most people may not like to read pages of code to find the problem for you.thefourtheye– thefourtheye2015年03月01日 14:24:28 +00:00Commented Mar 1, 2015 at 14:24
-
first time I am getting comments about to much code lol but ok trimmed it down hopefully that helps just kept the comment regarding what the error i get isFPcond– FPcond2015年03月01日 14:29:29 +00:00Commented Mar 1, 2015 at 14:29
-
@thefourtheye any thoughts on the issue?FPcond– FPcond2015年03月01日 14:41:07 +00:00Commented Mar 1, 2015 at 14:41
1 Answer 1
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);
}
});
}
3 Comments
for loops don't create a scope, all the functions ("tasks") created inside each iteration would reference the same info variable and end up with this problem: JavaScript closure inside loops forEach, in the other hand, takes a function callback which is executed immediately (synchronously) for each iteration. As the function callback is executed for each iteration, the function's parameters and variable declarations are scoped inside each iteration, and this solves the issue more cleanly than the aforementioned question's accepted answer.Explore related questions
See similar questions with these tags.