My first guess is that you're using markers
outside of the loop, for example in a call to the maps API. That won't work, as the data is loaded from Firestore asynchronously, and the data isn't available yet at that point.
The easiest way to understand how asynchronous loading works is with some well placed logging statements:
console.log("1. Before calling database");
db.collection('specials').get()
.then(snapshot => {
console.log("2. Got database results, calling geocode API");
snapshot.forEach(special => {
var location = special.data().address;
axios.get('https://maps.googleapis.com/maps/api/geocode/json?components=country:NZ|country:AU',{
params:{ address:location, key:'********' }
})
.then(function(response){
console.log("3. Got geocode result");
})
});
})
console.log("4. After calling database");
Now when you run this code, the logging output will be:
Before calling database
After calling database
Got database results, calling geocode API
Got geocode result
Got geocode result
...
This is probably not what you expected, as the code doesn't execute in the order in which you have it in your file. But it is completely working as intended, due to the nature of asynchronous APIs. And, it explains why locations from the database aren't added to maps, if your call to maps is near logging statement 4.
: by the time that runs, no data has been loaded from the database yet, and the geocoding hasn't been done yet either.
Any code that needs the data from the database should be inside the then
callback. To make things more complex: since you also want all geolookups to have been completed, you only want to add the markers to maps, once all the nested then()
calls have happened.
To do this you can use Promise.all
, which resolves a single then()
once multiple other then()
calls have been resolved.
Combining all of this, leads to code that should look something like this:
//Fetch all addresses from db
db.collection('specials').get()
.then(snapshot => {
// Get all addresses from the documents
return snapshot.docs.map(doc => doc.data().address);
})
.then(addresses => {
// Geocode all addresses, to get coordinates
return Promise.all(addresses.map(location => {
return axios.get('https://maps.googleapis.com/maps/api/geocode/json?components=country:NZ|country:AU',{
params:{
address:location,
key:'************************************',
}
})
});
})
.then(locations => {
// Convert all geometry into markers
return locations.map(response => {
markerLat = response.data.results[0].geometry.location.lat;
markerLng = response.data.results[0].geometry.location.lng;
return {
coords:{lat: markerLat, lng: markerLng}
});
});
})
.then(markers => {
// TODO: call the maps API and add the markers
});
If your code is running in a modern JavaScript environment, you can use async
/await
to make the above read a bit more normally.
//Fetch all addresses from db
let snapshot = await db.collection('specials').get()
// Get all addresses from the documents
let addresses = snapshot.docs.map(doc => doc.data().address);
// Geocode all addresses, to get coordinates
let locations = await Promise.all(addresses.map(location => {
return axios.get('https://maps.googleapis.com/maps/api/geocode/json?components=country:NZ|country:AU',{
params:{
address:location,
key:'************************************',
}
})
});
// Convert all geometry into markers
let markers = locations.map(response => {
markerLat = response.data.results[0].geometry.location.lat;
markerLng = response.data.results[0].geometry.location.lng;
return {
coords:{lat: markerLat, lng: markerLng}
});
});
// TODO: call the maps API and add the markers
As you can see here the structure is mostly the same, we mostly just undented the then
blocks by using the await
keyword.