Reputation:
I was having some problem with nested promise which resulting in forgotten promise problem.
let promiseList = new Promise((resolve, reject) => {
//first query to retrieve from firebase
query.once( 'value', data => {
var promises = [];
data.forEach(snapshot => {
//get item key
//second query based on item key
var promise = query.once('value');
promises.push(promise);
promise.then(data => {
var itemDetail = data.val();
var receiptID = itemDetail.receiptID;
// third query to find matching receiptID
var query = firebase.database().ref('receipts');
query.once('value', data => {
data.forEach(snapshot => {
snapshot.forEach(childSnapshot => {
if(childSnapshot.key == receiptID){
var branchDetail = childSnapshot.val().branch;
var branchName = branchDetail.branchName;
//console.log('inside promise ' + branchName);
datasetarr.push({branchName: branchName});
}
});
});
});
});
});
// wait till all promises are finished then resolve the result array
Promise.all(promises).then(() => resolve(datasetarr));
});
});
// print out array here
promiseList.then((arr) => {
for(var i = 0; i < arr.length; i++){
console.log(arr[i].branchName);
}
});
I managed to print out the data from the console.log with 'inside promise'. However, when I tried to print it out from the .then(), there is nothing shown.
The problem now is it actually ran the .then() first before I resolve the promise.
Any ideas?
Upvotes: 0
Views: 349
Reputation: 2835
I've never used Firebase, but I do know promises.
Check this sample chaining promises, notice the return
statements which produce the chaining.
var outerPromise = query.once('value').then(data => {
// Promise array to group 2nd level promises and then do a Promise.all.
var promises = [];
// This will be the main output of the outerPromise.
// We will populate it asynchronously inside our 2nd level promises.
var datasetarr = [];
data.forEach(snapshot => {
// 2nd level promises, will be appended to the promises array.
// and will be enchained with the 3d level promise.
var promise = query.once('value').then(data => {
var itemDetail = data.val();
var receiptID = itemDetail.receiptID;
var query = firebase.database().ref('receipts');
// Third level promise. It's enchained by the return statement.
return query.once('value').then(data => {
data.forEach(snapshot => {
snapshot.forEach(childSnapshot => {
if(childSnapshot.key == receiptID){
var branchDetail = childSnapshot.val().branch;
var branchName = branchDetail.branchName;
//console.log('inside promise ' + branchName);
datasetarr.push({branchName: branchName});
}
});
});
});
});
promises.push(promise);
});
// We wait until 2nd (and third) level promises are ready
// and the return our desired output, the datasetarr
return Promise.all(promises).then(()=> datasetarr);
});
// Since it's all chained, the outerPromise will resolve once all promises are completed
// and we can get the output we supplied in the last chaining.
outerPromise.then((arr) => {
console.log(arr)
});
Upvotes: 1
Reputation: 21926
That's not how promises work, there's seldom if ever a need to nest them. If query.once
already returns a promise that's great, but otherwise you'll need to wrap it:
let returnsPromise = value => new Promise(res => query.once(value, data => res(data));
Again, if it already returns a promise that's unnecessary, but I'm not a firebase guy. At any rate, now you can do something like this:
let result = returnsPromise('value')
// run secondary query based on item key
.then(data => Promise.all(data.map(item => returnsPromise(item.key)))
// now do stuff with those results
.then(data => {
return Promise.all(data.map(item => {
let receiptID = item.val().receiptID;
// Note that the same 'wrap if not already returning promise
// idea' is applicable here, but for illustration I'm just
// going to act like this returns a promise.
// Also note that while I've been rather down on nesting
// its more or less necessary here because you need to capture
// the receipt ID from the surrounding scope.
return firebase.database().ref('receipts')
.once('value')
.then(snapshot => {
return snapshot
.filter(x => x.key === receiptID)
.map(x => {
let branch = x.val().branch.branchName;
return {branch: branch};
});
});
}))
// Now we have an array of arrays of results but we want to
// remove the nesting.
.then(arrayOfArrays => arrayOfArrays.reduce((x,y) => { return x.concat(y); }, []));
Now you have a result promise that contains the array of values. You can call then
on it and iterate over it:
result.then(arr => arr.forEach(x => console.log(x.branchName)));
Upvotes: 0