Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Discord.js : Variable is undefined despite using await

I’m making a discord bot using discord.js that listens to slash commands in a discord server and manipulates data on a google sheet. I’ve come across a frustrating problem while trying to make a confirmation system for a command. The issue is with the getConfirmation() method. Here is the relevant code:

    async execute(interaction) {
        try {
            await interaction.deferReply();
               
            // get player from spreadsheet
            const iss = interaction.options.getInteger("iss");
            const player = await getPlayerByISS(iss);

            // get confirmation
            let hasConfirmed = await getConfirmation(interaction, player);

            if (!hasConfirmed) {
                await interaction.editReply("Draft choice abandoned.");
                return;
            }

            // if confirmed, draft the player
            let draftedPlayer = await draftPlayer(interaction.user.tag, player);

            await interaction.editReply(draftedPlayer);
        } catch (error) {
            console.log(error);
            await interaction.editReply("Could not draft player");
        }
    }

and

async function getConfirmation(interaction, player) {
    try {
        // send confirmation msg
        const message = await interaction.channel.send({
            content: `Are you sure you want to draft ${formatPlayerString(
                player
            )}?`
        });

        const filter = (reaction, user) => {
            return (
                ["👎", "👍"].includes(reaction.emoji.name) &&
                user.id === interaction.user.id
            );
        };

        // react to the msg to allow for easier reacting
        await Promise.all([message.react("👍"), message.react("👎")]);

        // awaitReactions just returns a list of all reaction objs that match above     
        // filter
        let didConfirm, responseMessage;
        message
            .awaitReactions({
                filter,
                max: 1,
                time: 60000,
                errors: ["time"],
            })
            .then((collected) => {
                const reaction = collected.first();

                didConfirm = reaction.emoji.name === "👍";
                responseMessage = didConfirm
                    ? "Confirmed. Drafting now..."
                    : "Cancelled confirmation.";
            })
            .catch((collected) => {
                console.log(collected);

                responseMessage =
                    "Did not receive response in time. Abandoning...";
                didConfirm = false;
            })
            .finally(async (collected) => {
                // clean up after 3s to prevent clutter
                setTimeout(() => message.delete(), 3000);

                // resolve the promise
                await message.edit(responseMessage);
                return didConfirm;
            });
    } catch (error) {
        console.log("Errored out : ", JSON.stringify(error));
        console.log(error);
        throw error;
    }
}

Sorry for the large chunk of code but let me explain. I want the bot to send a confirmation message, listen for any reaction replies to the message, and then proceed with drafting the player if the user who sent the /draft command reacts with a thumbs up emoji, and abandons otherwise.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

The issue is with the getConfirmation() method. Despite me having designated getConfirmation() as an async method, which should wrap my response in a promise and awaiting it, the code in execute() continues to run immediately after the await getConfirmation() line. It sets hasConfirmed = undefined and proceeds with that assumption. So no matter what the user reacts, the code will reach the !hasConfirmed block, and respond with "Draft choice abandoned".

However, this does not make any sense to me. Since getConfirmation() is an async method, and we are awaiting it in another async method, shouldn’t the await keyword halt execution until the promise has been fulfilled and hasConfirmed has a value? The confirmation message sent in getConfirmation does get correctly updated when the user reacts though, so I know that that code is being reached. Am I missing something obvious? Do I just fundamentally misunderstand asynchronous programming?

I’d really appreciate any help! Thanks a lot.

Edit: Phil’s answer was it

>Solution :

getConfirmation() doesn’t return anything so will always resolve to undefined. FYI the return value from finally() is not used to resolve the promise chain

// Quick demo showing what happens with finally
Promise.resolve(undefined).finally(() => "finally").then(console.log);

It’s also best not to mix async / await with .then() / .catch() as it can quickly get confusing.

I would instead structure the await reactions part of your code like this

let didConfirm, responseMessage;
try {
  const collected = await message.awaitReactions({
    filter,
    max: 1,
    time: 60000,
    errors: ["time"],
  });

  const reaction = collected.first();

  didConfirm = reaction.emoji.name === "👍";
  responseMessage = didConfirm
    ? "Confirmed. Drafting now..."
    : "Cancelled confirmation.";
} catch (err) {
  console.warn(err);
  responseMessage = "Did not receive response in time. Abandoning...";
  didConfirm = false;
}

// Things after an async try..catch is similar to finally
// only you can actually return a value

// clean up after 3s to prevent clutter
setTimeout(() => message.delete(), 3000);

// resolve the promise
await message.edit(responseMessage);
return didConfirm;
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading