Reputation: 752
I am struggling with returning a promise (or multiple promises) nested in a forEach
function, which is nested in a .then
function.
The second .then
does not wait for the promise inside the first .then
to complete and therefore does not return 'this.topics' (empty array instead) and logs undefined
instead of data.
I think it is best to wrap your head around by looking at the code, so here it is:
getTopics() {
this.$fireStore
.collection("courses")
.doc(this.$store.state.courses.currentlyPreviewing.cid)
.collection("sections")
.get()
.then(snapshot => {
snapshot.forEach(sec => {
return this.$fireStore
.collection("courses")
.doc(this.$store.state.courses.currentlyPreviewing.cid)
.collection("sections")
.doc(sec.id)
.collection("topics")
.orderBy("order")
.get()
.then(snapshot => {
snapshot.forEach(doc => {
this.topics.push({
data: doc.data(),
topic_id: doc.id,
sectionOfTopic_id: sec.id
});
});
console.log('First done!') // Only logged after data and this.topics
});
})
})
.then((data) => {
console.log(data) // Logs undefined
console.log(JSON.stringify(this.topics)) // Logs empty array
return this.topReady = true;
});
Upvotes: 2
Views: 153
Reputation: 368
You're working with a list or promises rather than a single promise, so take a look at using Promise.all
instead (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all).
The Promise.all() method returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled or when the iterable contains no promises. It rejects with the reason of the first promise that rejects.
getTopics() {
this.$fireStore
.collection("courses")
.doc(this.$store.state.courses.currentlyPreviewing.cid)
.collection("sections")
.get()
.then(snapshot => {
const promises = snapshot.map(sec => {
return this.$fireStore
.collection("courses")
.doc(this.$store.state.courses.currentlyPreviewing.cid)
.collection("sections")
.doc(sec.id)
.collection("topics")
.orderBy("order")
.get()
.then(snapshot => {
snapshot.forEach(doc => {
this.topics.push({
data: doc.data(),
topic_id: doc.id,
sectionOfTopic_id: sec.id
});
});
console.log('First done!') // Only logged after data and this.topics
});
})
return Promise.all(promises)
})
.then((data) => {
console.log(data) // Logs undefined
console.log(JSON.stringify(this.topics)) // Logs empty array
return this.topReady = true;
});
Upvotes: 3
Reputation: 13892
You didn't really specify which bit is the promise -- this.topics.push
? this.$fireStore...get()
?
I'm going to assume the latter one.
What you want to do is something more like this:
.then(snapshot => {
var promises = snapshot.map(sec => {
return this.$fireStore.
...
.get()
});
return Promise.all(promises);
});
When you do a .map
and return a promise in the callback, you make an array of promises. Promise.all
on an array of promises joins them into a single promise.
Upvotes: 2