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.
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;