Search code examples
javascriptfirebasereact-nativees6-promise

Trying to get Image-Url from Firebase using Promises and .then()


I'm trying to fetch some groups and their information. I need to do a join with firebase to get the last message in every group chat. I got this to work already. Now I also have to fetch the Group-Image Url from firebase storage. I'm trying this using a Promise and .then() to go on once the Promise resolved.

My thoughts were that for every group it would use the function getGroupImages() with the groupId to get the image. Once it has the image the code in the .then() would be fired.

Here is my code and what I'm trying to do:

export const getUserGroups = () => {
  return (dispatch) => {
    dispatch({ type: GET_USERS_GROUPS });
    const uid = firebase.auth().currentUser.uid;
    firebase.database().ref(`/userGroups/${uid}/`)
      .on('value', snapshot => {
        const promises = [];
        snapshot.forEach(child => {
          const children = child.val();
          const groupId = children.groupId;
          getGroupImages(groupId).then((image) => {
            children.image = image;
            const promise = firebase
              .database()
              .ref(`/groups/${groupId}/lastmessage`)
              .once('value')
              .then(snap => {
                children.lastmessage = snap.val();
                return children;
              });
              promises.unshift(promise);
          })
          .catch((e) => console.log(e));
        });
        Promise.all(promises)
        .then(groupArr => {
          dispatch({ type: GET_USERS_GROUPS_SUCCESS, payload: groupArr });
        })
        .catch((e) => console.log(e));
      });
  };
};

And this is the getGroupImages() function I wrote:

const getGroupImages = (groupId) => {
  return new Promise(() => (resolve, reject) => {
    const ref = firebase.storage().ref(`images/${groupId}/groupImg`);
    ref.getDownloadURL()
      .then((image) => {
        resolve(image);
      })
      .catch((e) => reject(e));
  })
  .catch((err) => console.log(err));
};

I'm pretty new to using promises and I'm trying to learn it but here I really have no idea what I'm doing wrong.

EDIT2:* I got something working but I think I created a monster. I don't think this is the way it should be:

export const getUserGroups = () => {
  return (dispatch) => {
    dispatch({ type: GET_USERS_GROUPS });
    const uid = firebase.auth().currentUser.uid;
    firebase.database().ref(`/userGroups/${uid}/`)
      .on('value', snapshot => {
        const promises = [];
        const groupArr = [];
        const messages = [];
        snapshot.forEach(child => {
          const children = child.val();
          const groupId = children.groupId;
          const promise = getGroupImages(groupId)
          .then(url => {
            children.img = url;
            return children;
          })
          .catch(e => console.log(e));
          promises.unshift(promise);
        }); // </foreach>
        Promise.all(promises)
        .then(children => { // child === array with both objects
          const messages = [];
          children.map(child => {
            const groupId = child.groupId;
            const ch = child;
            const message = getLastMessages(groupId)
            .then(msg => {
              ch.lastMsg = msg;
              return ch;
            })
            .catch((e) => console.log(e));
            messages.unshift(message);
          }); // .map
          Promise.all(messages)
          .then((bla) => {
            dispatch({ type: GET_USERS_GROUPS_SUCCESS, payload: bla });
          });
        });
      });
  };
};

Edit 1 With the help of "harshiL shah" I managed to get my promise to work. However the groupArr remains empty even if there is an image coming back from the promise.

snapshot.forEach(child => {
 const children = child.val();
 const groupId = children.groupId;
 getGroupImages(groupId).then((image) => {
  console.log(image); // url is showing
  children.image = image;
  const promise = firebase
    .database()
    .ref(`/groups/${groupId}/lastmessage`)
    .once('value')
    .then(snap => {
     children.lastmessage = snap.val();
     return children;
    });
     promises.unshift(promise);
    });
    });
    Promise.all(promises)
    .then(groupArr => {
    console.log('groupArr');
    console.log(groupArr); // [] empty Array
    dispatch({ type: GET_USERS_GROUPS_SUCCESS, payload: groupArr });

})


Solution

  • You are passing () => (resolve, reject) => { some code } in the promise, which is actually

    function() {
        return function(resolve, reject) {
            some code
        }
    }
    

    what actually needs to be done is

    function(resolve, reject) {
        some code
    }
    

    i.e. (resolve, reject) => { some code }

    Use the below code instead.

    const getGroupImages = (groupId) => {
        return new Promise((resolve, reject) => {
            const ref = firebase.storage().ref(`images/${groupId}/groupImg`);
            ref.getDownloadURL()
            .then((image) => {
                resolve(image);
                })
            .catch((e) => reject(e));
        })
        .catch((err) => console.log(err));
    };
    

    This is the only problem I can find in your code. I would have commented it but I don't have enough reputation.

    -------------- Answer for new edit ----------------

    For understanding the problem, say getting image from firebase is taking 1 min of time. snapshot.foreach would complete its execution even before getGroupImages() completes fetching first image, only after which it enters then block and promise of getting message is added in the promises array. so when Promise.all(promises) is called, promises is an empty array and you get empty array(groupArr) in then block. For verification of this theory you can print promises array before Promise.all().

    Below is the fix that should work in this case.

    snapshot.forEach(child => {
        const children = child.val();
        const groupId = children.groupId;
        promise = getGroupImages(groupId)
        .then((image) => {
          children.image = image;
          return firebase
            .database()
            .ref(`/groups/${groupId}/lastmessage`)
            .once('value');
        })
        .then(snap => {
          children.lastmessage = snap.val();
          return children;
        })
        .catch((e) => console.log(e));
        promises.unshift(promise);
      });
    

    I don't have any way to verify working of above code. so, treat it as pseudo code and make adjustments required.