user4815703
user4815703

Reputation:

Array filling up with Pending Promises?

I am so close to finishing the base of this function but i am stuck, and its a little todo with my lack of knowledge in promises and i am learning how they function etc. Regarding my issue, i am looping through some data that is then being stored in an Array that will be passed to another function via promise chain, however the array is being filled with pending promises where i just need the actual data from the promise placed into the array, please see below to understand whats going on:

var getGameData = function(matchIdArray) {
var promise = new Promise(function(resolve,reject) {
  console.log('Start getGameData');
  s3.headObject({Bucket: 'lolsearchgames', Key: '347341'}, function(err, result) {
    console.log('Checking Bucket');
    if (err && err.code === 'NotFound') {
      console.log('Bucket data Not Found!')
      var gameData = new Array();
      for (var i = 0; i < matchIdArray.length; i++) {
        gameData.push(new Promise(function(res, rej) {
          lolapi.Match.get(matchIdArray[i], function (error, gamedata) {
            if (error) {
                rej(error);
            }
            if (gamedata) {
                console.log('Pushing new data to Array! ' + gamedata.matchId);
                res(gamedata);
            }
          })
        }));
      }
      if (gameData.length === 10) {
          console.log(gameData);
          resolve(gameData);
      }
    } else {
      console.log('Bucket data Found!');
    }
  })
});
return promise;
console.log('End getGameData');
};

Upvotes: 0

Views: 939

Answers (1)

Jaromanda X
Jaromanda X

Reputation: 1

Using Promise.all to wait for all the "gameData" as follows - also use Array#map to avoid having to push to an array

Note: however, that if there's any rejection in any lolapi.Match.get promise, the Promise.all will reject - this may not be what you want, I'm not sure from your code example

var getGameData = function getGameData(matchIdArray) {
  var promise = new Promise(function (resolve, reject) {
    console.log('Start getGameData');
    s3.headObject({ Bucket: 'lolsearchgames', Key: '347341' }, function (err, result) {
      console.log('Checking Bucket');
      if (err && err.code === 'NotFound') {
        console.log('Bucket data Not Found!');
        Promise.all(matchIdArray.map(function(matchId) {
          return new Promise(function (res, rej) {
            lolapi.Match.get(matchId, function (error, gamedata) {
              if (error) {
                rej(error);
              }
              res(gamedata);
            });
          })
        }))
        .then(resolve)
        .catch(reject);
      } else {
        console.log('Bucket data Found!');
        reject('Bucket data Found!');
      }
    });
  });
  console.log('End getGameData');
  return promise.then(function (results) {  // see note below
    return results.filter(function (result) {
      return result;
    });
  });
};

Alternative - and I think "tidier" code

var getGameData = function getGameData(matchIdArray) {
    return new Promise(function(resolve, reject) {
        console.log('Start getGameData');
        s3.headObject({
            Bucket: 'lolsearchgames',
            Key: '347341'
        }, function(err, result) {
            console.log('Checking Bucket');
            if (err && err.code === 'NotFound') {
                console.log('Bucket data Not Found!');
                resolve();
            } else {
                console.log('Bucket data Found!');
                reject('Bucket data Found!');
            }
        });
    }).then(function() {
        return Promise.all(matchIdArray.map(function(matchId) {
            return new Promise(function(res, rej) {
                lolapi.Match.get(matchId, function(error, gamedata) {
                    if (error) {
                        rej(error);
                    }
                    res(gamedata);
                });
            })
        }));
    }).then(function (results) {  // see note below
        return results.filter(function (result) {
            return result;
        });
    });
}

If you use Bluebird promises, it has utility methods to "promisify" node callback style functions - in the code below, s3.headObjectAsync is the promisified version of s3.headObject and lolapi.Match.getAsync is the promisified version of lolapi.Match.get

You'll see, using bluebirds promisify, (and ES2015+ arrow notation, since you are using node) makes your code far more compact

var getGameData = function getGameData(matchIdArray) {
    return s3.headObjectAsync({
        Bucket: 'lolsearchgames',
        Key: '347341'
    })
    // do some gymnastics, because if there's no error, that should reject
    // but if the error is "NotFound" that should resolve
    .then(results => {
        throw {code: 'Bucket data Found!'};
    })
    .catch(err => {
        if (err && err.code === "NotFound") {
            return; // this is OK
        }
        throw err;
    })
    .then(() => Promise.all(matchIdArray.map(matchId => lolapi.Match.getAsync(matchId))))
    .then(results => results.filter(result => result)); // see note below
}

In ES5 code the above is still pretty compact

var getGameData = function getGameData(matchIdArray) {
    return s3.headObjectAsync({
        Bucket: 'lolsearchgames',
        Key: '347341'
    }).then(function (results) {
        throw { code: 'Bucket data Found!' };
    }).catch(function (err) {
        if (err && err.code === "NotFound") {
            return; // this is OK
        }
        throw err;
    }).then(function () {
        return Promise.all(matchIdArray.map(function (matchId) {
            return lolapi.Match.getAsync(matchId);
        }));
    }).then(function (results) {  // see note below
        return results.filter(function (result) {
            return result;
        });
    });
};

Please note in all these answers, the result would not be exactly what you would expect given your original code. In your original code, if gamedata was falsey you just ignored that "Match.get" ... however, you can't do that with Promises - the last .then in the answers filters out the empty results in the promise returned by getGameData

Upvotes: 2

Related Questions