Axios GET Request won't execute in a while loop - javascript

When I run node index.js with this code, everything works appropriately. What it's doing is getting a random coordinate with the randomGeo function and then converting that into an address with the Axios call. Lastly, this transPredtoJSON function is called in the .then of the Axios call, which takes the address and adds it to a JSON file. The problem arises when I want to execute this more than once, which I'll show in the next code snippet.
randomCoordinate = randomGeo({ latitude: originlat, longitude: originlng }, radius)
lat = randomCoordinate.latitude
lng = randomCoordinate.longitude
reverseGeoCodeURL = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${GEOCODE_API_KEY}`
// get closest address to the coordinate
axios.get(reverseGeoCodeURL).then(res => {
let ranAddr = res.data.results[0].formatted_address
let correctCity = /, Southlake,/i
if (correctCity.exec(ranAddr)) { // only Southlake addresses
transPredtoJSON(ranAddr) // are added to the output json file
}
}).catch(error => {
console.error('error: ', error);
})
Below I'm wrapping the above code in a while loop. I'll have a counter that will keep track of how many addresses were added to the output file and stop when it reaches its limit. Besides, axios.get never gets called bc the console log only outputs count: 0 infinitely. The weird thing is, when I move count += 1 right after the axios call so that the count increments whether or not the address is added, axios.get gets called and adds the addresses to the file like it's supposed to. Why doesn't Axios run in this while loop?
let enoughAddresses = 10
let count = 0
while (count < enoughAddresses) {
console.log('count', count);
randomCoordinate = randomGeo({ latitude: originlat, longitude: originlng }, radius)
lat = randomCoordinate.latitude
lng = randomCoordinate.longitude
reverseGeoCodeURL = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${key}`
axios.get(reverseGeoCodeURL).then(res => {
console.log('inside axios .then');
let ranAddr = res.data.results[0].formatted_address
let corrCity = /, Southlake,/i
if (corrCity.exec(ranAddr)) {
transPredtoJSON(ranAddr)
count += 1 // only increment count when an address has been added
console.log('count', count);
}
}).catch(error => {
console.error('error: ', error);
})
}

The problem is because the axios function is running async. You can probably use a promise to solve this.
this pseudo code might point you in the right direction:
let enoughAddresses = 10
for (i = 0; i < enoughAddresses; i++) {
addresses.push(new Promise((r, j) => {
// your async function to get addresses
});
}
// returns a promise when all your async functions are done
Promise.all(addresses).then((values) => {
// values should contain your addresses
});
also, this for reference.

Related

For loop index variable is getting mutated by parallel events - Node js

Main task of this function is to fetch device data array from a json file and mutate deviceLocation & upTime attributes in each child objects and return the mutated device array. Original deviceLocation property has values of longitudes & latitudes separated by Z. For example, 81.003Z6.764. This value will be split and sent to the reverseGeocode() async function that reverse geocode the coordinates using google map API and returns the result. calculateUpTime() function is just to convert seconds into days, hours, minutes & seconds.
Express route calls the getMainLocationsList() function. Then it calls the getLocationByDeviceName(deviceName) function which then finally calls the getDeviceData() function.
Issue happens when getDeviceData() function is getting executed to serve the above mentioned express request, if another identical request will be made. What I have found so far by debugging is that for loop index i is getting accessed and mutated by both events.
I want to avoid such multiple events from trying to access & mutate the index i concurrently.
const getDeviceData = async () => {
let deviceData = await JSON.parse(JSON.stringify(fetchDeviceData()));
for (let i = 0; i < deviceData.length; i++) {
const deviceLocation = deviceData[i].deviceLocation;
const [long, lat] = deviceLocation.split("Z");
const locationName = await reverseGeoCode(long, lat);
deviceData[i].deviceLocation = {
long: long,
lat: lat,
name: locationName,
};
outdoorDevices = deviceData[i].outdoorDevices;
for (let j = 0; j < outdoorDevices.length; j++) {
const deviceLocation = outdoorDevices[j].deviceLocation;
const [long, lat] = deviceLocation.split("Z");
const locationName = await reverseGeoCode(long, lat);
outdoorDevices[j].deviceLocation = {
long: long,
lat: lat,
name: locationName,
};
try {
outdoorDevices[j].upTime = calculateUpTime(
parseInt(outdoorDevices[j].upTime)
);
} catch {
outdoorDevices[j].upTime = "N/A";
}
}
}
return deviceData;
};

async functions not executing in the correct order inside a map function

I have created an async function that will extra the data from the argument, create a Postgres query based on a data, then did some processing using the retrieved query data. Yet, when I call this function inside a map function, it seemed like it has looped through all the element to extra the data from the argument first before it proceed to the second and the third part, which lead to wrong computation on the second element and onwards(the first element is always correct). I am new to async function, can someone please take at the below code? Thanks!
async function testWeightedScore(test, examData) {
var grade = [];
const testID = examData[test.name];
console.log(testID);
var res = await DefaultPostgresPool().query(
//postgres query based on the score constant
);
var result = res.rows;
for (var i = 0; i < result.length; i++) {
const score = result[i].score;
var weightScore = score * 20;
//more computation
const mid = { "testID": testID, "score": weightScore, more values...};
grade.push(mid);
}
return grade;
}
(async () => {
const examSession = [{"name": "Sally"},{"name": "Bob"},{"name": "Steph"}]
const examData = {
"Sally": 384258,
"Bob": 718239,
"Steph": 349285,
};
var test = [];
examSession.map(async sesion => {
var result = await testWeightedScore(sesion,examData);
let counts = result.reduce((prev, curr) => {
let count = prev.get(curr.testID) || 0;
prev.set(curr.testID, curr.score + count);
return prev;
}, new Map());
let reducedObjArr = [...counts].map(([testID, score]) => {
return {testID, score}
})
console.info(reducedObjArr);
}
);
})();
// The console log printed out all the tokenID first(loop through all the element in examSession ), before it printed out reducedObjArr for each element
The async/await behaviour is that the code pause at await, and do something else (async) until the result of await is provided.
So your code will launch a testWeightedScore, leave at the postgresql query (second await) and in the meantime go to the other entries in your map, log the id, then leave again at the query level.
I didn't read your function in detail however so I am unsure if your function is properly isolated or the order and completion of each call is important.
If you want each test to be fully done one after the other and not in 'parallel', you should do a for loop instead of a map.

Creating a recursive function with async / await with setTimeout

I have a process in my code where I need to get a list of technician drive times. I use the Google Maps API to get the driving time between the origin and destination. As most of you know, the API requires to have a timeout of roughly 1 second or more to work without generating errors. I have created a recursive function to retrieve the list of times that I need using a setTimeout within the method, like so:
function GetTechDriveTimes(info, destAddress) {
let techs = this.state.Techs
.filter(tech => tech.Address != "" && !tech.Notes.includes('Not'))
.map(tech => {
let techObj = {
TechName: tech.FirstName + " " + tech.LastName,
TechAddress: tech.Address + " " + tech.City + " " + tech.State + " " + tech.Zip,
KioskID: info.ID.toUpperCase(),
DriveTime: "",
};
return techObj
});
let temp = [...techs]; // create copy of techs array
const directionsService = new google.maps.DirectionsService();
recursion();
let count = 0;
function recursion() {
const techAddress = temp.shift(); // saves first element and removes it from array
directionsService.route({
origin: techAddress.TechAddress,
destination: destAddress,
travelMode: 'DRIVING'
}, function (res, status) {
if (status == 'OK') {
let time = res.routes[0].legs[0].duration.text;
techs[count].DriveTime = time;
} else {
console.log(status);
}
if (temp.length) { // if length of array still exists
count++;
setTimeout(recursion, 1000);
} else {
console.log('DONE');
}
});
}
return techs;
}
After this method is complete, it will return an array with the techs and their respective drive times to that destination. The problem here is that using setTimeout obviously doesn't stop execution of the rest of my code, so returning the array of technicians will just return the array with empty drive times.
After timeout is complete I want it to return the array within the method it was called like this:
function OtherMethod() {
// there is code above this to generate info and destAddress
let arr = GetTechDriveTimes(info, destAddress);
// other code to be executed after GetTechDriveTimes()
}
I've looked online for something like this, and it looks like I would need to use a Promise to accomplish this, but the difference from what I found online is that they weren't using it inside of a recursive method. If anyone has any ideas, that would help me a lot. Thanks!
You could use promises, but you can also create a callback with the "other code to be executed after GetTechDriveTimes" and send it to the function:
function OtherMethod() {
// there is code above this to generate info and destAddress
// instead of arr = GetTechDriveTimes, let arr be the parameter of the callback
GetTechDriveTimes(info, destAddress, function(arr) {
// other code to be executed after GetTechDriveTimes()
});
}
function GetTechDriveTimes(info, destAddress, callback) {
...
if (temp.length) { // if length of array still exists
...
} else {
console.log('DONE');
callback(techs); // send the result as the parameter
}
...

How can I return different values from a function depending on code inside an Axios promise? NodeJS - a

I have a block of code that calls an Api and saves results if there are differences or not. I would like to return different values for DATA as layed out on the code. But this is obviously not working since Its returning undefined.
let compare = (term) => {
let DATA;
//declare empty array where we will push every thinkpad computer for sale.
let arrayToStore = [];
//declare page variable, that will be the amount of pages based on the primary results
let pages;
//this is the Initial get request to calculate amount of iterations depending on result quantities.
axios.get('https://api.mercadolibre.com/sites/MLA/search?q='+ term +'&condition=used&category=MLA1652&offset=' + 0)
.then(function (response) {
//begin calculation of pages
let amount = response.data.paging.primary_results;
//since we only care about the primary results, this is fine. Since there are 50 items per page, we divide
//amount by 50, and round it up, since the last page can contain less than 50 items
pages = Math.ceil(amount / 50);
//here we begin the for loop.
for(i = 0; i < pages; i++) {
// So for each page we will do an axios request in order to get results
//Since each page is 50 as offset, then i should be multiplied by 50.
axios.get('https://api.mercadolibre.com/sites/MLA/search?q='+ term +'&condition=used&category=MLA1652&offset=' + i * 50)
.then((response) => {
const cleanUp = response.data.results.map((result) => {
let image = result.thumbnail.replace("I.jpg", "O.jpg");
return importante = {
id: result.id,
title: result.title,
price: result.price,
link: result.permalink,
image: image,
state: result.address.state_name,
city: result.address.city_name
}
});
arrayToStore.push(cleanUp);
console.log(pages, i)
if (i === pages) {
let path = ('./compare/yesterday-' + term +'.json');
if (fs.existsSync(path)) {
console.log("Loop Finished. Reading data from Yesterday")
fs.readFile('./compare/yesterday-' + term +'.json', (err, data) => {
if (err) throw err;
let rawDataFromYesterday = JSON.parse(data);
// test
//first convert both items to check to JSON strings in order to check them.
if(JSON.stringify(rawDataFromYesterday) !== JSON.stringify(arrayToStore)) {
//Then Check difference using id, otherwise it did not work. Using lodash to help.
let difference = _.differenceBy(arrayToStore[0], rawDataFromYesterday[0],'id');
fs.writeFileSync('./compare/New'+ term + '.json', JSON.stringify(difference));
//if they are different save the new file.
//Then send it via mail
console.log("different entries, wrote difference to JSON");
let newMail = mail(difference, term);
fs.writeFileSync('./compare/yesterday-' + term +'.json', JSON.stringify(arrayToStore));
DATA = {
content: difference,
message: "These were the differences, items could be new or deleted.",
info: "an email was sent, details are the following:"
}
return DATA;
} else {
console.log("no new entries, cleaning up JSON");
fs.writeFileSync('./compare/New'+ term + '.json', []);
DATA = {
content: null,
message: "There were no difference from last consultation",
info: "The file" + './compare/New'+ term + '.json' + ' was cleaned'
}
return DATA;
}
});
} else {
console.error("error");
console.log("file did not exist, writing new file");
fs.writeFileSync('./compare/yesterday-' + term +'.json', JSON.stringify(arrayToStore));
DATA = {
content: arrayToStore,
message: "There were no registries of the consultation",
info: "Writing new file to ' " + path + "'"
}
return DATA;
}
}
})
}
}).catch(err => console.log(err));
}
module.exports = compare
So I export this compare function, which I call on my app.js.
What I want is to make this compare function return the DATA object, so I can display the actual messages on the front end,
My hopes would be, putting this compare(term) function inside a route in app.js like so:
app.get("/api/compare/:term", (req, res) => {
let {term} = req.params
let data = compare(term);
res.send(data);
})
But as I said, Its returning undefined. I tried with async await, or returning the whole axios first axios call, but Im always returning undefined.
Thank you

Firebase function execution and subscription to list that is being updated by a firebase function

I think a firebase function updating a list that I have in the firebase database is being captured by a subscription that is subscribed to that list. From what the list output looks like on my phone (in the app)...and from what my console output looks like (the way it repeats) it seems like it is capturing the whole list and displaying it each time one is added. So (I looked this up)...I believe this equation represents what is happening:
(N(N + 1))/2
It is how you get the sum of all of the numbers from 1 to N. Doing the math in my case (N = 30 or so), I get around 465 entries...so you can see it is loading a ton, when I only want it to load the first 10.
To show what is happening with the output here is a pastebin https://pastebin.com/B7yitqvD.
In the output pay attention to the array that is above/before length - 1 load. You can see that it is rapidly returning an array with one more entry every time and adding it to the list. I did an extremely rough count of how many items are in my list too, and I got 440...so that roughly matches the 465 number.
The chain of events starts in a page that isn't the page with the list with this function - which initiates the sorting on the firebase functions side:
let a = this.http.get('https://us-central1-mane-4152c.cloudfunctions.net/sortDistance?text='+resp.coords.latitude+':'+resp.coords.longitude+':'+this.username);
this.subscription6 = a.subscribe(res => {
console.log(res + "response from firesbase functions");
loading.dismiss();
}, err => {
console.log(JSON.stringify(err))
loading.dismiss();
})
Here is the function on the page with the list that I think is capturing the entire sort for some reason. The subscription is being repeated as the firebase function sorts, I believe.
loadDistances() {
//return new Promise((resolve, reject) => {
let cacheKey = "distances"
let arr = [];
let mapped;
console.log("IN LOADDISTANCES #$$$$$$$$$$$$$$$$$$$$$");
console.log("IN geo get position #$$$$$$$5354554354$$$$$$$");
this.distancelist = this.af.list('distances/' + this.username, { query: {
orderByChild: 'distance',
limitToFirst: 10
}});
this.subscription6 = this.distancelist.subscribe(items => {
let x = 0;
console.log(JSON.stringify(items) + " length - 1 load");
items.forEach(item => {
let storageRef = firebase.storage().ref().child('/settings/' + item.username + '/profilepicture.png');
storageRef.getDownloadURL().then(url => {
console.log(url + "in download url !!!!!!!!!!!!!!!!!!!!!!!!");
item.picURL = url;
}).catch((e) => {
console.log("in caught url !!!!!!!$$$$$$$!!");
item.picURL = 'assets/blankprof.png';
});
this.distances.push(item);
if(x == items.length - 1) {
this.startAtKey4 = items[x].distance;
}
x++;
})
//this.subscription6.unsubscribe();
})
}
The subscription in loadDistances function works fine as long as I don't update the list from the other page - another indicator that it might be capturing the whole sort and listing it repeatedly as it sorts.
I have tried as as I could think of to unsubscribe from the list after I update...so then I could just load the list of 10 the next time the page with the list enters, instead of right after the update (over and over again). I know that firebase functions is in beta. Could this be a bug on their side? Here is my firebase functions code:
exports.sortDistance = functions.https.onRequest((req, res) => {
// Grab the text parameter.
var array = req.query.text.split(':');
// Push the new message into the Realtime Database using the Firebase Admin SDK.
// Get a database reference to our posts
var db = admin.database();
var ref = db.ref("profiles/stylists");
var promises = [];
// Attach an asynchronous callback to read the data at our posts reference
ref.on("value", function(snapshot) {
//console.log(snapshot.val());
var snap = snapshot.val();
for(const user in snap) {
promises.push(new Promise(function(resolve, reject) {
var snapadd = snap[user].address;
console.log(snapadd + " snap user address (((((((())))))))");
if(snapadd != null || typeof snapadd != undefined) {
googleMapsClient.geocode({
address: snapadd
}).asPromise()
.then(response => {
console.log(response.json.results[0].geometry.location.lat);
console.log(" +++ " + response.json.results[0].geometry.location.lat + ' ' + response.json.results[0].geometry.location.lng + ' ' + array[0] + ' ' + array[1]);
var distanceBetween = distance(response.json.results[0].geometry.location.lat, response.json.results[0].geometry.location.lng, array[0], array[1]);
console.log(distanceBetween + " distance between spots");
var refList = db.ref("distances/"+array[2]);
console.log(snap[user].username + " snap username");
refList.push({
username: snap[user].username,
distance: Math.round(distanceBetween * 100) / 100
})
resolve();
})
.catch(err => { console.log(err); resolve();})
}
else {
resolve();
}
}).catch(err => console.log('error from catch ' + err)));
//console.log(typeof user + 'type of');
}
var p = Promise.all(promises);
console.log(JSON.stringify(p) + " promises logged");
res.status(200).end();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
});
What is weird is, when I check the firebase functions logs, all of this appears to only run once...but I still think the subscription could be capturing the whole sorting process in some weird way while rapidly returning it. To be as clear as possible with what I think is going on - I think each stage of the sort is being captured in an (N(N + 1))/2...starting at 1 and going to roughly 30...and the sum of the sorting ends up being the length of my list (with 1-10 items repeated over and over again).
I updated to angularfire2 5.0 and angular 5.0...which took a little while, but ended up solving the problem:
this.distanceList = this.af.list('/distances/' + this.username,
ref => ref.orderByChild("distance").limitToFirst(50)).valueChanges();
In my HTML I used an async pipe, which solved the sorting problem:
...
<ion-item *ngFor="let z of (distanceList|async)" no-padding>
...

Categories