Hello I'm having some issues with Firebase Functions, but the issue is most likely more so on the JavaScript side. You see I need a function to return to the client an array of objects that are within the range of a client's location. The problem is that this function needs to iterate and test the documents in a collection to see if they're within range. If it is, push the object onto the array. Once it finishes iterating each thing, return the array back to the client.
The main issue is that JavaScript isn't synchronous when it comes to loops. So my next idea was to try to force waiting after the iteration was finished using promises. Down below is the code I currently have:
export const getNearbyBison = functions.https.onCall((data, context) => {
let distance = data.distance;
let thisUid = data.uid;
let Lat1 = data.x;
let Lon1 = data.y;
let returnUIDs: any[] = [];
console.log(distance, thisUid, Lat1, Lon1);
var needDocs = admin.firestore().collection('users').doc('user').collection('user').where('locationEnabled', '==', true).get();
return needDocs.then(documents => {
console.log(documents);
let theseDocs = documents.docs;
let promiseCheck: any[] = [];
theseDocs.forEach(doc => {
let pUid = doc.data().uid;
console.log('Uid of this user' + pUid);
admin.firestore().collection('location').doc(pUid).get().then(async location => {
if (location.exists) {
console.log('Location of this user' + location);
let locID = location.id;
let thisData = location.data();
let Lat2 = thisData!.x;
let Lon2 = thisData!.y;
console.log(locID, Lat2, Lon2);
//**********Location equation to solve distance*********
var R = 6371000;
var phi1 = Lat1 * Math.PI / 180;
var phi2 = Lat2 * Math.PI / 180;
var deltaPhi = Lat2 - Lat1;
var deltaLambda = Lon2 - Lon1;
var dLat = deltaPhi * Math.PI / 180;
var dLon = deltaLambda * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(phi1) * Math.cos(phi2) * Math.sin(dLon / 2) *
Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
//*******Convert to miles**************
var miles = d / 1609.34;
console.log('Miles of user: ' + miles);
let pusher = new Promise((resolve, reject) => {
if (miles <= distance) {
if (thisUid != locID) {
//Remember this setup for when you have to use the info for this page...
returnUIDs.push({
farAway: miles,
userInfo: doc
});
resolve();
} else { resolve() }
} else { resolve() }
});
promiseCheck.push(
pusher
)
}
}).catch();
})
Promise.all(promiseCheck).then(whatever => {
console.log(returnUIDs);
return ({
nearbyArray: returnUIDs
});
}).catch();
}).catch();
});
The problem is that the function is still sending the array to the client before the forEach loop finishes iterating. Is there a way I can force it to wait? What am I doing wrong? I have looked at other similar issues on this forum and that's how I got to the conclusion that I need to use Promises in some way to cause the program to wait for the loop to be complete. Thank you in advance.
Edit:: Okay so I made a fair bit of progress. For one I put everything that's within the forEach() loop into a returnable Promise and then pushed each one onto the Promise array. I looked up how Javascript deals with asynchronous operations and that was the reason why it was logging an empty array to my console before the loops were finished. I checked my console and now it does log the returned array AFTER the forEach loops finish. Here's the edited code:
export const getNearbyBison = functions.https.onCall((data, context) => {
let distance = data.distance;
let thisUid = data.uid;
let Lat1 = data.x;
let Lon1 = data.y;
let returnUIDs: any[] = [];
console.log(distance, thisUid, Lat1, Lon1);
var needDocs = admin.firestore().collection('users').doc('user').collection('user').where('locationEnabled', '==', true).get();
needDocs.then(documents => {
console.log(documents);
let theseDocs = documents.docs;
let promiseCheck: any[] = [];
theseDocs.forEach(doc => {
let pusher = new Promise((resolve, reject) => {
let pUid = doc.data().uid;
console.log('Uid of this user' + pUid);
admin.firestore().collection('location').doc(pUid).get().then(location => {
if (location.exists) {
console.log('Location of this user' + location);
let locID = location.id;
let thisData = location.data();
let Lat2 = thisData!.x;
let Lon2 = thisData!.y;
console.log(locID, Lat2, Lon2);
//**********Location equation to solve distance*********
var R = 6371000;
var phi1 = Lat1 * Math.PI / 180;
var phi2 = Lat2 * Math.PI / 180;
var deltaPhi = Lat2 - Lat1;
var deltaLambda = Lon2 - Lon1;
var dLat = deltaPhi * Math.PI / 180;
var dLon = deltaLambda * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(phi1) * Math.cos(phi2) * Math.sin(dLon / 2) *
Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
//*******Convert to miles**************
var miles = d / 1609.34;
console.log('Miles of user: ' + miles);
if (miles <= distance) {
if (thisUid != locID) {
//Remember this setup for when you have to use the info for this page...
returnUIDs.push({
farAway: miles,
userInfo: doc
});
resolve();
} else { resolve() }
} else { resolve() }
}
}).catch();
});
promiseCheck.push(pusher);
});
Promise.all(promiseCheck).then(whatever => {
console.log(returnUIDs);
return ({
nearbyArray: returnUIDs
});
}).catch();
}).catch();
});
The problem now is that it's STILL returning empty information to the client. Not only that but this empty information is being sent before the loops are finished. So on my console I can see that the array the function should return is populated with the correct information to send back. The problem is however that the function is still sending nothing, I suspect because it's still sending before the loops are finished. Can anyone tell me why this is the case?