0

I have been referencing the first and second answer from this thread to try and introduce async functions into my program. I am trying to gather user input before building an embed to then send back out to my discord server. I have tried a few different ways but am not making any progress. Here is what I have right now:

///// Lets start here
    execute(message, args) 
    {
        embedBuilder(message);
    },
};

// Collector to collect the users input and return it to some variable
async function collector(message,limit) 
{
    message.channel.awaitMessages(response => response.author.id === message.author.id, 
        {
            max: 1,
            time: 10000,
            errors:['time'],
        })
        .then((collected) => {
            if (collected.first().content.length < limit)
            {
                message.author.send(`I collected the message : ${collected.first().content}`);
                return collected.first().content;
            }
            //else
            collector(limit);
        })
        .catch(() => {
            message.author.send("No message collected after 10 seconds.")
        })
}

async function embedBuilder(message)
{
    message.author.send("Lets get to work!\nPlease enter the title of your event. (Must be shorter than 200 characters)");
    const title = await collector(message,200); // AWAIT HERE
    message.author.send("Please enter a short description of your event. (Must be shorter than 2000 characters)");
    const description = await collector(message,2000); // AWAIT HERE
    const eventEmbed = new Discord.MessageEmbed()
    .setColor('RANDOM')
    .setTitle(title)
    .setAuthor(message.author.username)
    .setDescription(description)
    .setImage();
    message.channel.send(eventEmbed);
}

Right now it is not waiting at all, plowing through both of my prompts to the user and then running 2 collectors at once, so when I type something in, both collectors return the same thing.

For example:

Me : !plan //Prompting the discord command
Bot: Lets get to work!
     Please enter the title of your event. (Must be shorter than 200 characters)
     Please enter a short description of your event. (Must be shorter than 2000 characters)
Me : Testing
Bot: I collected the message : testing
     I collected the message : testing

Can anyone point out what I am doing wrong? I believe I may be misunderstanding something about how async functions work in JS, but I feel like I followed the correct syntax based off of the answers I looked at from the linked post.

Thanks for any help.

Caleb Renfroe
  • 183
  • 1
  • 13

2 Answers2

2

You need to return your Promises

async function collector(message,limit) 
{
    return message.channel....
            //else
            return collector(limit);
Snow
  • 3,820
  • 3
  • 13
  • 39
  • Hey! This worked, thanks a ton. I was wondering if you would be able to explain the concept to me though? I don't know what I was doing wrong and am new to the concept of async functions and promises in JavaScript. I think I understand that the awaits in my embed function wait for the collector to return an answer/promise. However, I don't think I followed how the promises were working in my collector function. Thanks for any extra help and thanks again for the solution. :) – Caleb Renfroe Sep 25 '20 at 18:03
  • You have to use `await` in an async function to wait for a Promise to resolve. If you're in an async function and simply *create* a Promise, but you don't return it or `await` it, no logic gets connected to when the Promise resolves. By returning or awaiting the Promise, you let the caller of the function use the result of the Promise. – Snow Sep 25 '20 at 21:41
0

Actually your question related to asynchronous programming in javascript and to implement this you need first understand the way of implementation of them

you have multiple ways to achieve waiting function to execute some code after that function finishes:

  • using callbacks In this fashion you can create a function that contains the code you want to be executed after finishing another code

Example:

const func =(...args,cb) => {
  console.log('execute this code first')
  console.log(4 * 4);
}
const callback = () => {
  console.log("call this after finishing the previous code")
}
  • The second thing is using the new async code in javascript which is called promises, the concept is promising is so simple, you run some code in non-blocking code with the availability to call another function in case of the code success or failing

This can be applied in two ways:

1- then & catch

const successCb = () => {
 console.log("success");
}
const failCb = (err) => {
 console.log("failed", err)
}
message.channel.awaitMessages().then(successCb).catch(failCb)

2- the second way is using async/await fashion for more cleaner code

async function myFunc() {
  try {
     await message.channel.awaitMessages();
     console.log("process finished, continue execution")
  } catch(err) {
    console.log("failed")
  }
}

In your question, you need firstly specify what the code should be executed first and the code should be executed then, so we have two methods:

  • collector (called first)
  • embed builder (executed later)

collector (invalid way)

async function collector(message,limit) 
{
    message.channel.awaitMessages(response => response.author.id === message.author.id, 
        {
            max: 1,
            time: 10000,
            errors:['time'],
        })
        .then((collected) => {
            if (collected.first().content.length < limit)
            {
                message.author.send(`I collected the message : ${collected.first().content}`);
                return collected.first().content;
            }
            //else
            collector(limit);
        })
        .catch(() => {
            message.author.send("No message collected after 10 seconds.")
        })
}

you have here some conflicts, since you defined the parent function will work in an async way so you need to return a promise inside this method or using await inside it to let the interpreter that this function executes promises inside it but you didn't do that, you use promise. then inside an async function and when you use it inside the embedBuilder function you await this parent function but not await the actual promise, so the promise is executed while another code is also being executed

Collector (valid way)

you have two choices either return promise or using await inside the function as the following:

async function collector(message,limit) {
   try {
     let collected = await message.channel.awaitMessages(response => response.author.id === message.author.id, 
        {
            max: 1,
            time: 10000,
            errors:['time'],
        })
        if (collected.first().content.length < limit)
            {
                message.author.send(`I collected the message : ${collected.first().content}`);
                return collected.first().content;
            }
            //else
            collector(limit);
   } catch(err) {
     message.author.send("No message collected after 10 seconds.")
   }
}

Then you can call it inside the builder as you did

tarek salem
  • 540
  • 1
  • 6
  • 20