Scott Scott - 2 months ago 7
Node.js Question

Promise.all() behaving unexpectedly, where is my lack of understanding?

I am trying to write a script which uses the GitHub API. I have a function that accepts a list of usernames. For each username a call is made to the API to get that users starred repos. For each users starred repo i'd like to capture the name of the repo and the number of stars, eventually I'd like to do more work with this data.

I'm trying to implement this with just native promises and avoid Q, Bluebird etc. Here's what I have that is not working.

function getNameAndStarInfo(repo){
return new Promise(function(resolve, reject){
//i'd like this to return an object but it has to be an iterable??
resolve([{[repo.full_name] : repo.stargazers_count}]);
});
};

function getStarredRepos(usernames){
var promises =[];
for (var user in usernames){
//build the header for API request

var name = usernames[user];
var url = 'https://api.github.com/users/' + name + '/starred';
var header = {url: url, headers: {'User-Agent': 'username', 'Authorization': 'token blahblabhlabh'}, json: true };

//for the current user make a request and get their starred repos
request(header, function(err, res, usersStarredRepos){
for (var repo in usersStarredRepos){
promises.push(getNameAndStarInfo(usersStarredRepos[repo]));

};
});
};
Promise.all(promises)
.then(function(promises){
//dont need to log, would like to do stuff with this data later
console.log(promises);
});

};


Essentially, I'm expecting to see a all of the {name: stargazers_count} data when I log the array after in the .then() method. However an empty array is output every time. Could someone explain to me what I'm missing here?

Answer

You are pushing into promises only when a request is already done. So when Promise.all is being executed, the array is still empty because none of them has finished yet.

Try with this way:

function getNameAndStarInfo(repo) {
    return new Promise(function(resolve, reject) {
        //i'd like this to return an object but it has to be an iterable??
        resolve([{
            [repo.full_name]: repo.stargazers_count
        }]);
    });
}

function getUserStars(username) {
    return new Promise(function(resolve, reject) {
        var name = username.name;
        var url = 'https://api.github.com/users/' + name + '/starred';
        var header = { url: url, headers: { 'User-Agent': 'username', 'Authorization': 'token blahblabhlabh' }, json: true };
        //for the current user make a request and get their starred repos
        request(header, function(err, res, usersStarredRepos) {
            var promises = [];
            for (var repo in usersStarredRepos) {
                promises.push(getNameAndStarInfo(usersStarredRepos[repo]));
            };
            Promise.all(promises).then(resolve).catch(reject);
        });
    });
}

function getStarredRepos(usernames) {
    var promises = [];
    for (var user in usernames) {
        promises.push(getUserStars(usernames[user]));
    }
    Promise.all(promises).then(console.log).catch(console.log);
}

But to avoid creating Promises manually to handle requests, i would suggest you to use request-promise:

https://www.npmjs.com/package/request-promise