Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for all firebase call to finish with map function

I'm fetching my user data and the map function is called several times for each user. I want to wait until all data was pushed to the array and then manipulate the data. I tried using Promise.all() but it didn't work. How can I wait for this map function to finish before continuing?

Needless to say that the array user_list_temp is empty when printed inside the Promise.all().

 const phone_list_promise_1 = await arrWithKeys.map(async (users,i) => {
      return firebase.database().ref(`/users/${users}`)
          .on('value', snapshot => {
              user_list_temp.push(snapshot.val());
              console.log(snapshot.val());
            })
        }
  );

Promise.all(phone_list_promise_1).then( () => console.log(user_list_temp) )

I changed the code to this but I still get a wrong output

Promise.all(arrWithKeys.map(async (users,i) => {
      const eventRef = db.ref(`users/${users}`);
      await eventRef.on('value', snapshot => {
        const value = snapshot.val();
        console.log(value);
        phone_user_list[0][users].name = value.name;
        phone_user_list[0][users].photo = value.photo;
      })
      console.log(phone_user_list[0]); 
      user_list_temp.push(phone_user_list[0]);
    }

  ));
    console.log(user_list_temp); //empty  array
}
like image 260
Soragim Avatar asked Feb 04 '19 21:02

Soragim


2 Answers

It is possible to use async/await with firebase

This is how I usually make a Promise.all

const db = firebase.database();
let user_list_temp = await Promise.all(arrWithKeys.map(async (users,i) => {
    const eventRef = db.ref(`users/${users}`);
    const snapshot = await eventref.once('value');
    const value = snapshot.value();
    return value;
  })
);

This article gives a fairly good explanation of using Promise.all with async/await https://www.taniarascia.com/promise-all-with-async-await/

Here is how I would refactor your new code snippet so that you are not mixing promises and async/await

let user_list_temp = await Promise.all(arrWithKeys.map(async (users,i) => {
  const eventRef = db.ref(`users/${users}`);
  const snapshot=  await eventRef.once('value');
  const value = snapshot.val();
  console.log(value);
  phone_user_list[0][users].name = value.name;    // should this be hardcoded as 0?
  phone_user_list[0][users].photo = value.photo;  // should this be hardcoded as 0?
  console.log(phone_user_list[0]); 
  return phone_user_list[0];        // should this be hardcoded as 0?
  })
);
console.log(user_list_temp); 

Here is a simple example that uses fetch, instead of firebase:

async componentDidMount () {
  let urls = [
    'https://jsonplaceholder.typicode.com/todos/1',
    'https://jsonplaceholder.typicode.com/todos/2',
    'https://jsonplaceholder.typicode.com/todos/3',
    'https://jsonplaceholder.typicode.com/todos/4'
  ];
  let results = await Promise.all(urls.map(async url => {
    const response = await fetch(url);
    const json = await response.json();
    return json;
  }));
  alert(JSON.stringify(results))
}
like image 97
Andrew Avatar answered Nov 09 '22 04:11

Andrew


If I understand your question correctly, you might consider revising your code to use a regular for..of loop, with a nested promise per user that resolves when the snapshot/value for that user is available as shown:

const user_list_temp = [];

/*
Use regular for..of loop to iterate values
*/
for(const user of arrWithKeys) {

    /*
    Use await on a new promise object for this user that
    resolves with snapshot value when value recieved for
    user
    */
    const user_list_item = await (new Promise((resolve) => {

        firebase.database()
        .ref(`/users/${users}`)
        .on('value', snapshot => {

            /*
            When value recieved, resolve the promise for
            this user with val()
            */    
            resolve(snapshot.val());
        });
    }));

    /*
    Add value for this user to the resulting user_list_item
    */
    user_list_temp.push(user_list_item);
}

console.log(user_list_temp);

This code assumes that the enclosing function is defined as an asynchronous method with the async keyword, seeing that the await keyword is used in the for..of loop. Hope that helps!

like image 1
Dacre Denny Avatar answered Nov 09 '22 03:11

Dacre Denny