Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript return function happens too late

What my code should do

I am creating a social media app using react native, and I have run into an problem with my code. I am trying to create a function which grabs all of the posts from all of the user's groups. To do this, I created a loop. The loop repeats once for every group the user is in. Every one loop gets the posts from one group. Every time the loop is called, a new function is called which gets the posts from a new group the user is in, then returns the posts back to the original function, which adds them to a full list of posts.

Problem

The function getting the posts doesn't return the posts. I think that the code is not waiting for the posts to be returned, and moves on. Basically, when I console.log the posts on the function that got them, I get the correct posts, but when I console.log the entire list of posts, I get nothing back.

My question

How can I wait for a value to be returned by a function, and not have the code instantly move on?

My code

        runQ(group){
//this function actually gets the posts from the server (from firebase)
    var items = [];
    firebase.database().ref('posts/'+group).limitToLast(
           Math.floor(24/this.state.passGroups.length)*this.state.numOfPosts
        ).orderByKey().once ('value', (snap) => {
          snap.forEach ( (child) => {       
           items.push({
             //post info
         });
         });
    this.setState({passItems: items})
    console.log(items); //logs correct items.
    }).then(()=>{
       if( this.state.passItems.length != 0 ){return this.state.passItems;}
    })
    }


    //gets the user's groups, then sends out a request to each group for the newest posts.
      getItems(){
      //gets groups...
      //...    
    .then(()=>{
    var allItems = [];
    //allItems will be the big list of all of the posts.
    for (i = 0; i < this.state.passGroups.length; i++) {
       //sending request to runQ function to get posts.
       allItems.push(this.runQ(this.state.passGroups[i].name)) //allItems logs as allItems: ,,,,
    }
    })
    }

2 Answers

Use async-await to make the for loop wait for each response.

First, you need to return the promise created by your Firebase call (you currently don't return anything from your runQ() function).
Change this line:

firebase.database().ref('posts/'+group).limitToLast(

into:

return firebase.database().ref('posts/'+group).limitToLast(

Then tell your callback to the getItems() call to be an async function and await each response from runQ():

  getItems(){
    //gets groups...
    //...    
    .then(async () => {
      var allItems = [];
      for (var i = 0; i < this.state.passGroups.length; i++) {
        allItems.push(await this.runQ(this.state.passGroups[i].name)) 
      }
    })
  }
like image 54
Lennholm Avatar answered Mar 12 '26 10:03

Lennholm


First you have to return a promise from runQ like this: return firebase.database.....

Then in the for loop you can do like this:

let allPromises = []
for (i = 0; i < this.state.passGroups.length; i++) {
  allPromises.push(this.runQ(this.state.passGroups[i].name))
}
Promise.all(allPromises).then(itemsArray => {
  allItems = allItems.concat(itemsArray)
})

Make sure that you have allItems declared in the right scope.

like image 42
Bamse Avatar answered Mar 12 '26 09:03

Bamse



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!