Below is how I generally use promises and I haven't had any issues thus far until now. The problem here is that the console.log(this.recipe)
is undefined and when I console.log(JSON.stringify(recipes))
it shows me its an empty array. This leads me to believe that I am not resolving the nested promises correctly.
ngOnInit(): void {
this.recipeService.getAllRecipesByUserId().then((recipes) => {
this.allRecipes = recipes;
this.recipe = this.allRecipes[0];
console.log(this.recipe);
});
}
The getAllRecipesByUserId()
function should return a promise of type Recipe[]
async getAllRecipesByUserId(): Promise<Recipe[]> {
let recipes: Recipe[] = [];
await this.userService.userRecipeIds().then((ids) => {
ids.forEach((id) => {
const q = query(this.recipeCollection, where('id', '==', id));
getDocs(q).then((querySnapshot) => {
querySnapshot.forEach((doc) => {
recipes?.push(doc.data() as Recipe);
});
});
});
});
return recipes;
}
userRecipeIds():
async userRecipeIds(): Promise<string[]> {
let user: User = new User();
const q = query(
this.userCollection,
where('userId', '==', this.auth.getCurrentUser()?.uid)
);
return await getDocs(q).then((querySnapshot) => {
querySnapshot.forEach((doc) => (user = doc.data() as User));
return user.recipes;
});
}
Am I resolving the promises correctly here ?
EDIT:
I have edited the userRecipeIds()
method to the following:
async userRecipeIds(): Promise<string[]> {
const q = query(
this.userCollection,
where('userId', '==', this.auth.getCurrentUser()?.uid)
);
const docs = await getDocs(q);
const user = docs.docs[0].data() as User;
return user.recipes;
}
And then subsequently refactored the getAllRecipesByUserId() to the following:
async getAllRecipesByUserId(): Promise<Recipe[]> {
let recipes: Recipe[] = [];
const userRecipes = await this.userService.userRecipeIds();
userRecipes.forEach(async (recipeId) => {
const q = query(this.recipeCollection, where('id', '==', recipeId));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
recipes.push(doc.data() as Recipe);
});
});
return recipes;
}
I still seem to run into the same problem where the array seems to be empty upon inspection within the ngOnInit()
.
CodePudding user response:
I don't fully understand your code, but for instance this snippet looks wrong. You are iterating over an array an assign each item to a single field called user
, so at the end, after the forEach will take place, field user will have the value of the last item, so the last doc
as you call it.
Maybe the whole method should look more like this:
async userRecipeIds(): Promise<Recipe[]> {
const q = query(
this.userCollection,
where('userId', '==', this.auth.getCurrentUser()?.uid)
);
const docs = await getDocs(q);
const recipes = docs.map(doc => doc.data());
return recipes;
}
in case we only want first element:
async userRecipeIds(): Promise<User> {
const q = query(
this.userCollection,
where('userId', '==', this.auth.getCurrentUser()?.uid)
);
const docs = await getDocs(q);
const user = docs[0].data();
return user;
}
Then that's how the other function can maybe look like?:
async getAllRecipesByUserId(): Promise<Recipe[]> {
const recipes: Recipe[] = await this.userService.userRecipeIds();
return recipes;
}
Answer to edited question: When you need to resolve multiple promises at once, for instance when the promises are created from items of an array. You need to treat the results asynchronously. The foreach method doesn't care about that and will end asap, without waiting for results from your promises inside. What you should do is use Promise.all method. Store all promises, and resolve them all, making an array of results:
async getAllRecipesByUserId(): Promise<Recipe[]> {
const userRecipes = await this.userService.userRecipeIds();
const recipieQueries = userRecipes.map(recipe => {
const q = query(...));
return getDocs(q);
})
const recipes = await Promise.all(allRecipiesPromises);
return recipes;
}