Firestore get subcollection data with promises - javascript

I'm trying to get the data from my subcollections, the things is I need to do it with Promises (If I don't, I can't get the data from the cache)
Here how I am actually doing :
bookStores = db.collection("bookstores");
bookStores.onSnapshot((snapshot) => {
snapshot.docChanges().forEach((change) => {
bookStoresIds.push(change.doc.id); // I use a list so a can iterate on IDs for subs
// Doing stuff
});
bookStoresIds.forEach(bookStoreId => {
const task = db.collection('bookstores').doc(bookStoreId).collection('books')
task.onSnapshot((snapshotTask) => {
snapshotTask.docChanges().forEach((change) => {
// Doing stuff
});
});
})
I use a list to store the IDs. This version works, but causes me some troubles and I want to use Promises.
Here what I tried :
async function getBookStores(id,) {
const bookStoreIds: string[] = [];
db.collection("bookStores").onSnapshot({ includeMetadataChanges: true }, (snapshot) => {
snapshot.docChanges().forEach((change) => {
// Doing Stuff
});
});
return bookStoreIds;
}
async function getBooks(bookStoreIds) {
bookStoreIds.forEach(bookStoreId => {
const book = db.collection('bookStores').doc(bookStoreId).collection('books')
task.onSnapshot({ includeMetadataChanges: true }, (snapshotTask) => {
snapshotTask.docChanges().forEach((change) => {
// Doing Stuff
});
})
})
}
getBookStores(id)
.then((list) => {
return getBooks(list);
})
The problem is, when it cames to getBooks, the list is empty ... Is somebody have an idea ? 🙏

Related

Using fetch and randomuser.me, how do I return multiple results?

I'm trying to use randomuser.me and I have the fetch request set up properly. I'm returning a single user. However, I want to return 5 comma separated users.
According to randomuser.me's documentation, I only need to append the fetch URI with ?results=5 (or any number I choose) and the multiple users are returned.
I've done this in the snippet below, but I'm still only returning a single user.
How do I return a comma separated result of 5 users?
window.onload = () => {
randomUserGenerator();
};
const randomUserGenerator = () => {
fetch("https://randomuser.me/api/?results=5")
.then((res) => {
return res.json();
})
.then((data) => {
showRandomUserData(data);
});
};
showRandomUserData = (randomUser) => {
document.getElementById("name").innerText =
`${randomUser.results[0].name.first} ${randomUser.results[0].name.last}`;
};
<h3><u>Users:</u> <span id="name"></span></h3>
using results[0] ... you're only USING the first user
Something like this may help you along
window.onload = () => {
randomUserGenerator();
};
const randomUserGenerator = () => {
fetch("https://randomuser.me/api/?results=5")
.then((res) => {
return res.json();
})
.then((data) => {
showRandomUserData(data);
});
};
showRandomUserData = (randomUser) => {
// combine all users
const users = randomUser.results.map(({name: { first, last}}) => `${first} ${last}`).join(', ');
// show them
document.getElementById("name").innerText = users;
};
<h3><u>Users:</u> <span id="name"></span></h3>

Async request to firebase inside forEach

I have an array like that (generatedArray):
[
{
...some data,
[deals],
type: 'uo'
},
{
...some data,
[deals],
type: 'pif',
pifKua: 'some value'
},
...other similar objects
]
If type is 'pif' I want to take snapshot from firebase and put snapshot's value in pifKua field. To do it I have a such function:
handleCreateDocs = () => {
this.generatedArray.forEach( item => {
console.log('itemtype', item.type);
if (item.type === 'pif') {
const stocksRef = firebase.database().ref('clients');
const kua = stocksRef.orderByChild('shortName').equalTo(item.pifKua);
kua.once('value').then( snapshot => {
snapshot.forEach((child) => {
Object.assign(item.pifKua, child.val());
});
})
}
});
this.setState({
kuaCheck: true
}, () => {console.log('kua', this.generatedArray)});
};
But after this function this.generatedArray don't changes. In console i can see 'kua', but this.generatedArray still the same witjout any changes. Help me to fix it please.
You should only call setState until you have all the data available. Right now, you are calling it without waiting for any of the queries to complete. then does not cause your code to pause and wait for the result. It just returns another promise that resolves some time later while your code continues in the forEach loop.
You will need to collect your promises into an an array, and use Promise.all() to wait for them to complete. The general form of your code will be more like this:
handleCreateDocs = () => {
const promises = [];
this.generatedArray.forEach( item => {
console.log('itemtype', item.type);
if (item.type === 'pif') {
const stocksRef = firebase.database().ref('clients');
const kua = stocksRef.orderByChild('shortName').equalTo(item.pifKua);
const promise = kua.once('value').then( snapshot => {
snapshot.forEach((child) => {
Object.assign(item.pifKua, child.val());
});
});
promises.push(promise);
}
});
Promise.all(promises).then(() => {
console.log("all queries complete");
this.setState({
kuaCheck: true
}, () => {console.log('kua', this.generatedArray)});
});
};

How to use foreach and promise

I need to get datas with nested foreach, but I can't fill my array.
At the end of this code I would like to have an array (segId) with my datas but it is empty (because of aynschronous).
I read that I had to use Promise.all but I can't beacause my promise are nested
I'm beginner so my code is far from perfect
How can I do that ?
async function getActivities(strava, accessToken)
{
const payload = await strava.athlete.listActivities({'access_token':accessToken, 'after':'1595281514', 'per_page':'10'})
return payload;
}
async function getActivity(strava, accessToken, id)
{
const payload = await strava.activities.get({'access_token':accessToken, 'id':id, 'include_all_efforts':'true'})
return payload;
}
async function getSegment(strava, accessToken, id)
{
const payload = await strava.segments.get({'access_token':accessToken,'id':id})
return payload
}
var tableau = []
var segId = []
const activities = getActivities(strava, accessToken)
activities.then(value => {
value.forEach((element, index) => {
const activity = getActivity(strava, accessToken, element['id'])
activity.then(value => {
value['segment_efforts'].forEach((element, index) => {
const segment = getSegment(strava, accessToken, element['segment']['id'])
segment.then(value => {
segId.push(value['id'])
})
//console.log(segId)
});
});
})
}) console.log(segId)
Regards
PS : Sorry for my english ...
Something like this should work. You need to always return the inner promises to include them in your promise chain. Consider splitting the code into functions to make it more readable.
getActivities(strava, accessToken).then(activities => {
return Promise.all(activities.map(elem => {
return getActivity(strava, accessToken, elem['id']).then(activity => {
return Promise.all(activity['segment_efforts'].map(elem => {
return getSegment(strava, accessToken, elem['segment']['id']).then(segment => {
segId.push(segment['id']);
});
}));
})
}));
})
.then(_ => {
console.log(segId);
});

React & Firebase - making multiple Firebase calls and waiting for all promises to be complete

I would like to change the following code as it reaches Firebases 10 item in an array limit. I want to loop through all teamIds and make a Firebase query for each individual teamId. The issue is, I'm not sure how to do this in a way that it waits until all promises are complete before continuing.
This is the current code;
const unsubscribe = Firebase.firestore().collection('invites').where('teamId', 'in', teamIds).onSnapshot(snapshot => {
const invites = [];
snapshot.docs.forEach(doc => {
const data = doc.data();
if (!invites[data.teamId]) {
invites[data.teamId] = [];
}
invites[data.teamId].push(Object.assign({}, { id: doc.id }, doc.data()));
});
setTeamInvites(invites);
setLoading(false);
setError(false);
});
I've like to change it to something like this;
teamIds.forEach(teamId => {
Firebase.firestore().collection('invites').where('teamId', '==', teamId).onSnapshot(snapshot => {
// Map the results to an array that will be stored in the pageState when all promises are complete
});
});
How can I do this?
I figured out I can do this with Promise.all, this is what I ended up with.
const promises = [];
teamIds.forEach(teamId => {
promises.push(new Promise((resolve, reject) => {
Firebase.firestore().collection('invites').where('teamId', '==', teamId).onSnapshot(snapshot => {
let invites = [];
invites[teamId] = [];
snapshot.docs.forEach(doc => {
const data = doc.data();
invites[data.teamId].push(Object.assign({}, { id: doc.id }, data));
resolve(invites);
});
});
}));
});
Promise.all(promises).then(allInvites => {
// Do what I needed to do with all of the invites here
});

.then() fires before previous .then() has returned

I'm pulling category information from our local point of sale database (3rd party software) and trying to write it into a WooCommerce store. I'm also storing the data to my own database to maintain relationships between the two systems. I'm using promises to sequence everything, but one of my .then() statements is firing off before the previous .then() has returned, so I'm sending an empty payload to WooCommerce.
router.post("/:action", (req, res) => {
if(req.params.action === "sync" && req.body.action === "sync") {
// Query the POS database
mssql.query(query, (err, result) => {
let postData = {
create: [],
update: []
}
// Make some promises to pull the POS categories and their children
Promise.all(promises)
.then(cats => {
let catPromises = cats.map(cat => {
return new Promise((resolve, reject) => {
Category.findOne(
// Check for existing entry in the linking DB...
)
.then(data => {
// ...and handle accordingly
resolve()
})
.then(() => {
let childPromises = cat.children.map(child => {
return new Promise((resolve, reject) => {
Category.findOne(
// Checking for existing entry in the linking DB...
)
.then(data => {
// ...and handle accordingly
resolve()
})
})
})
Promise.all(childPromises)
.then(resolved => {
resolve()
})
})
})
})
Promise.all(catPromises)
.then(() => {
return
})
})
.then(() => {
// This is the part that's firing early
return axios.post(
// data
)
})
...
EDIT: Newly refactored, still having problems.
Promise.all(promises).then(cats => {
let catPromises = cats.map(cat => {
Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
}).then(() => {
let childPromises = cat.children.map(child => {
Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
})
})
return Promise.all(childPromises)
})
})
// Now this is where we're reaching early
return Promise.all(catPromises)
}).then(() => {
// API call
})
Final solution:
Promise.all(promises).then(cats => {
let catPromises = cats.map(cat => {
return Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
}).then(() => {
let childPromises = cat.children.map(child => {
return Category.findOne(
// Check for existing...
).then(data => {
// ...and handle accordingly
})
})
return Promise.all(childPromises)
})
})
// Now this is where we're reaching early
return Promise.all(catPromises)
}).then(() => {
// API call
})

Categories