4

I am trying to return Array of tokens stored in Firebase, and I am using 'promise'.

function getUsersTokens() {
 let dbRef = db.ref('/system/users');
 let result = new Promise((resolve, reject) => {
    dbRef.once('value', (snap) => {
        let tokens = [];
        snap.forEach(child => {
            if(child.Status != "occupied"){
                helper.getToken(child.key,db).then(function(token){
                    tokens.push(token);
                });
            }       
        }); 
        resolve(tokens);
    }, (err) => {
        reject(err);
    });
 });
 return result;
  }

and this is the 'getToken' method from the "helper" module.

exports.getToken=function(uid,db){
   return db.ref(`/Tokens/${uid}`).once('value').then(function(result){
     return result.val();
   });
};

The problem is that every time I push token into the array it all works fine, but when exit getUsersTokens() the array gets empty.

thanks for the help.

3 Answers 3

2

The issue is that your result promise is resolving too early because the helper.getToken() is non-blocking, so your forEach will finish running before all of the getToken() calls have finished pushing their token into tokens.

To make things a little easier, you can split your result promise into two promises. The first promise will be in charge of getting snap. The second promise will be in charge of iterating through snap to produce an array of tokens:

function getUsersTokens() {

    let dbRef = db.ref('/system/users');

    let result = new Promise((resolve, reject) => {
        dbRef.once('value', (snap) => {
            resolve(snap);
        }, (err) => {
            reject(err);
        });
    });

    return result.then(snap => {

        let prommiseArr = [];

        snap.forEach(child => {
            if(child.Status != "occupied"){
                let p = helper.getToken(child.key,db);
                promiseArr.push(p);
            }  
        });

        return Promise.all(promiseArr); // resolves to array of tokens

    });
}

Promise.all takes in an array of promises, and resolves when all of those promises have also resolved. the promise returned by getUsersToken will ultimately contain an array of tokens, because each promise of promiseArr resolves to a token.

Sign up to request clarification or add additional context in comments.

Comments

1

It happens because the promise is resolved with the token array before getToken() resolves itself. You see an empty array because your handler runs before the tokens arrive.

You need to wait on that before resolving. Like this:

function getUsersTokens() {
  let dbRef = db.ref('/system/users');
  return new Promise((resolve, reject) => {
    dbRef.once('value', (snap) => {
      const tokensPromise = snap
        .filter(child => child.Status !== "occupied")
        .map(child => helper.getToken(child.key, db));
      resolve(Promise.all(tokensPromise));
    });
  });
}

Comments

1

Promise.all as pointed out by @André Werlang and @Christian Santos it perfect here is an example using reduce way

function getUsersTokens() {
 let dbRef = db.ref('/system/users');
 let result = new Promise((resolve, reject) => {
    dbRef.once('value', (snap) => {
       snap.reduce((chain, child) => {
          return chain.then(array => {
             return helper.getToken(child.key,db).then(function(token){
                return array.push(token);
              });
          });
       }, Promise.resolve([])).then(tokens=>{
                    resolve(tokens);
                });
    }, (err) => {
        reject(err);
    });
 });
 return result;
  }

1 Comment

it wouldn't work because tokens are pushed onto the array at a later time. also Promise.resolve([]) is tricky and relies on the fact that resolving a promise isn't immediate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.