maria maria -4 years ago 77
Javascript Question

Node.js using multiple promises

I currently try to use two kind of promises. One for the overall convensation, and one to get the userids to usernames.

The code works when its ran first time, and is looking something like this:enter image description here

The output above is what i wanted.

But when i run the query again, i get this:enter image description here

what is wrong with my code? what do i need to do to make the promises go correctly?

code:

function getconvensations(userid) {
return new Promise(function(resolve, reject) {

convensations.find({members: userid}).lean().then(function (convensations) {
var promises = convensations.map(function (currentConvensation) {
return new Promise(function (resolve, reject) {
var realnames = [];
var usernames = currentConvensation.members.map(function (CurrentUser) {
Core.userDetails(CurrentUser).then(function (user) {
realnames.push(user.username);
console.log("RESOLVING USERNAMES");
resolve();
});
});

Promise.all(usernames).then(function () {
console.log("GET LATEST MESSAGE");
latestMessage(currentConvensation._id).then(function (message) {
console.log("Messages: ");
console.log(message);
currentConvensation.spesificmessage = message;
currentConvensation.users = realnames;
currentConvensation.otherend = (currentConvensation.members[0] == userid ? realnames[0] : realnames[1]);

console.log("RESOLVE LATEST MESSAGE");


resolve();
});
});
});
});

Promise.all(promises).then(function () {
console.log("FINISHED CONVENSATIONS");
console.log("-------------------------");
console.log(convensations);
console.log("-------------------------");
return resolve(convensations);
}).catch(console.error);
});
});
}


caller:

} else if(action == 'getconvensations') {
Messages_model.getconvensations(req.user._id).then(function (response) {
res.json(response);
});
}

Answer Source

You have a race condition here:

new Promise(function (resolve, reject) {
    …
    currentConvensation.members.map(function (CurrentUser) {
        Core.userDetails(CurrentUser).then(function (user) {
            resolve();
        });
    });
    …
    latestMessage(currentConvensation._id).then(function (message) {
        resolve();
    });
    …
});

There are arbitrarily many tasks executing concurrently, and the first promise that fulfills will call resolve().

The solution is to avoid the Promise constructor antipattern and never ever call new Promise or resolve manually! Instead, chain promise callbacks to each other using the then method, and return new promises from every function to allow the caller to wait for them.

function getconvensations(userid) {
    return convensations.find({members: userid}).lean().then(function (convensations) {
//  ^^^^^^
        var promises = convensations.map(function (currentConvensation) {
            var usernamepromises = currentConvensation.members.map(function (CurrentUser) {
                console.log("GETTING USERNAME");
                return Core.userDetails(CurrentUser).then(function (user) {
//              ^^^^^^
                    console.log("FULFILLED USERNAME");
                    return user.username;
//                  ^^^^^^
                });
            });
            return Promise.all(usernamepromises).then(function (realnames) {
//          ^^^^^^
                console.log("FULFILLED ALL USERNAMES");
                currentConvensation.users = realnames;
                currentConvensation.otherend = (currentConvensation.members[0] == userid ? realnames[0] : realnames[1]);

                console.log("GETTING LATEST MESSAGE");
                return latestMessage(currentConvensation._id);
//              ^^^^^^
            }).then(function (message) {
                console.log("FULFILLED LATEST MESSAGE");
                console.log("Message: ", message);
                currentConvensation.spesificmessage = message;
            });
        });
        return Promise.all(promises).then(function () {
//      ^^^^^^
            console.log("FINISHED ALL CONVENSATIONS");
            console.log("-------------------------");
            console.log(convensations);
            console.log("-------------------------");
            return convensations;
//          ^^^^^^
        });
    });
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download