0

I'm trying to write chained-promises to perform multiple queries on the users ranking in various leagues, as well as gathering user data. But I don't understand how to join all the things i've gathered into a single coherent output.

This is the code I have so far:

const user = new User();
    Users.getUserByName(name).then(function (foundUser) {
        if (foundUser == null)
            throw new Error("no user found");

        // This is actually where the user is first found not the empty one above...

    })
    .then(Users.getUserTeamRank(name).then(function (teamRank) {
        user.teamRank = teamRank;
        console.log("TEAM_RANK " + teamRank);
    }))
    .then(Users.getUserSoloRank(name).then(function (soloRank) {
        user.soloRank = soloRank;
        console.log("SOLO_RANK " + soloRank);
    }))
    .then(Users.getUserFFARank(name).then(function (ffaRank) {
        user.ffaRank = ffaRank;
        console.log("FFA_RANK " + ffaRank);
    }))
    .catch(err => {
        if (err.message === "no user found") {
            msg.channel.send(MessageUtils.error("No player by the name {" + name + "} was found."));
            return;
        }
    });
    // these vars are undefined for examople
    console.log(user.teamRank + " , " + user.ffaRank + ", " + user.soloRank);

The problem I'm having is that after all the promises have been executed the user.teamRank is undefined. How to proceed from here?

Tiago Redaelli
  • 560
  • 5
  • 17

2 Answers2

3

There are a number of problems with the code you've written.

  1. First, this code doesn't need to be serialized. Since none of the operations seem to depend on the ones that came before (except the very first one), you can run these all in parallel which will likely be faster and can use a different structure.

  2. If you had properly chained/sequenced your results, you could view the final user object with all the properties in the last .then() handler (not outside of it).

  3. You are passing a promise to a .then() handler. That doesn't work properly. You need to pass a function reference to a .then() handler so the promise infrastructure can call that function LATER. You are executing it immediately and passing the returned promise to .then() which does nothing useful.

For sequencing multiple operations and collecting all the data, see this other answer that covers all the possibilities: How to chain and share prior results with promises.

Here's one way to run the last three operations in parallel:

Users.getUserByName(name).then(function (foundUser) {
    if (foundUser == null)
        throw new Error("no user found");

    return Promise.all([
        Users.getUserTeamRank(name), 
        Users.getUserSoloRank(name),
        Users.getUsreFFARank(name)
    ]).then([teamRank, soloRank, ffaRank] => {
        foundUser.teamRank = teamRank;
        foundUser.soloRank = soloRank;
        foundUser.ffaRank = ffaRank;

        // use the foundUser object here
        console.log(foundUser);
    });

}).catch(err => {
    if (err.message === "no user found") {
        msg.channel.send(MessageUtils.error("No player by the name {" + name + "} was found."));
        return;
    }
});

If you still wanted to sequence the operations (which will look a little simpler, but likely be slower to complete), I'd use async/await:

async function getUserStuff() {
    try {
        let foundUser = await Users.getUserByName(name);
        if foundUser === null) {
            throw new Error("no user found");
        }
        foundUser.teamRank = await Users.getUserTeamRank(name);
        foundUser.soloRank = await Users.getUserSoloRank(name);
        foundUser.ffaRank = await Users.getUserFFARank(name);

        // can use the foundUser object here
        console.log(foundUser);

        return foundUser;
    } catch(err) {
        if (err.message === "no user found") {
            msg.channel.send(MessageUtils.error("No player by the name {" + name + "} was found."));
            throw err;
        }
    }
}

If you wanted to call this function and use the result it returned, it returns a promise so you'd use it like this:

 getUserStuff(name).then(user => {
     // user object with all the new properties available here
 }).catch(err => {
     // handle error here
 });
jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

Chaining promises looks a bit clumsy, better to use async await, await makes sure that the response has come and then the next lines are executed. Just like the then block, which is executed once the response is available.

(async () => {
    try {
        const user = new User();
        const foundUser await Users.getUserByName(name)
        if (foundUser == null)
            throw new Error("no user found");

        const teamRank = await Users.getUserTeamRank(name)
        user.teamRank = teamRank;
        console.log("TEAM_RANK " + teamRank);
        const soloRank = await Users.getUserSoloRank(name)
        user.soloRank = soloRank;
        console.log("SOLO_RANK " + soloRank);
        const ffaRank = await Users.getUserFFARank(name);
        user.ffaRank = ffaRank;
        console.log("FFA_RANK " + ffaRank);
        // these vars are undefined for examople
        console.log(user.teamRank + " , " + user.ffaRank + ", " + user.soloRank);
    } catch(error) {
        console.log(error)
    }

})();

or use promise.all to increase the performance and look the code look neat as well -

(async () => {
    try {
        const user = new User(),
              foundUser = await Users.getUserByName(name);
        if (foundUser == null)
            throw new Error("no user found");
         const [teamRank, soloRank, ffaRank] = await Promise.all([Users.getUserByName(name), Users.getUserTeamRank(name), Users.getUserSoloRank(name), Users.getUserFFARank(name) ]) 
        user.teamRank = teamRank;
        user.soloRank = soloRank;
        user.ffaRank = ffaRank;
        console.log(user.teamRank + " , " + user.ffaRank + ", " + user.soloRank);
    } catch(error) {
        console.log(error)
    }

})();
Nayan Patel
  • 1,683
  • 25
  • 27
  • 1
    I think you're missing that it's the `foundUser` object that the OP wants to add properties to (at least that's my interpretation of the question) and they want to check it for `null` before proceeding with the other operations. – jfriend00 Aug 31 '19 at 17:20