Darius
Darius

Reputation: 1

Discord.js message collector in a loop for tic tac toe

So i started to make a discord bot using discord.js which has a command for tic tac toe. I tried using a message collector, like this in a while loop. After it enters the loop nothing happens. I added a console.log to see if the totalRouds variable is modifying and it was, so the loop somehow passed over the message collector code and executed just the last line.

while(numberOfrounds < 9){
      const filter = m => m.author.id === message.author.id;
      message.reply('Enter a position:');
      message.channel.awaitMessages(filter, {max: 1, time: 3000, errors: ['time']}).then(collected => {

      // code for drawing a canvas 

      }).catch(err => {
              console.log(err);
      });
      console.log(totalRouds);
      numberOfrounds ++;
}

Upvotes: 0

Views: 392

Answers (1)

Cloud
Cloud

Reputation: 1159

.awaitMessages() returns a Promise that resolves into your collected message data asynchronously. That means that while your last log statement runs immediately after you start collecting messages, the collected messages are only available and thus processed at a later time.

To illustrate that, let me tidy your code a little and add some logs in the order that your code would normally run:

while (numberOfrounds < 9) {
    console.log("1");
    const filter = m => m.author.id === message.author.id;
    message.reply('Enter a position:');
    console.log("2");
    message.channel.awaitMessages(
        filter,
        { max: 1, time: 3000, errors: ['time'] }
    ).then(collected => {
        console.log("5, after resolving, the collected data is only now avaiable");
    }).catch(err => {
        console.log(err);
    });
    console.log("3");
    console.log(totalRouds);
    numberOfrounds++;
    console.log("4, immediately starts the next loop, back to #1 again");
}

I presume that your totalRouds variable is defined within the callback function passed into .then(). So other than the typo (let's fix that), your variable would be defined in the wrong scope and thus totalRounds would always remain undefined, even after the Promise resolves with your collected messages, you process those messages, you set totalRounds within the callback function etc. So here is our updated snippet:

while (numberOfrounds < 9) {
    const filter = m => m.author.id === message.author.id;
    message.reply('Enter a position:');

    message.channel.awaitMessages(
        filter,
        { max: 1, time: 3000, errors: ['time'] }
    ).then(collected => {
        let totalRounds = 1 // Do your processing here
        console.log(totalRounds); // This should log as expected
    }).catch(err => {
        console.log(err);
    });
    numberOfrounds++;
}

But this is still probably not the behaviour you are looking for. Why? Your bot will attempt to send all 9 replies (assuming your numberOfRounds starts at 0 earlier) at once, at which point Discord.js will automatically send them in batches to avoid spamming the API, and all message collectors would be waiting concurrently. You probably intend to "pause" or suspend processing until the Promise returned by .awaitMessages() resolves and you have finished processing the returned data, causing synchronous behaviour whilst using an asynchronous method call (therefore you say "the loop somehow passed over the message collector code and executed just the last line"). To do this, we can use async-await:

/*
    You have not provided your full code,
    so what you need to do is mark your message event's
    callback function as async.
    Refer to the linked article on MDN.
*/

while (numberOfrounds < 9) {
    const filter = m => m.author.id === message.author.id;
    message.reply('Enter a position:');

    /*
        Then, wait for the Promise returned by
        this promise chain to resolve
        before resuming operation
        and moving on to the next iteration.
    */
    await message.channel.awaitMessages(
        filter,
        { max: 1, time: 3000, errors: ['time'] }
    ).then(collected => {
        let totalRounds = 1 
        console.log(totalRounds);
    }).catch(err => {
        console.log(err);
    });
    numberOfrounds++;
}

My terminology may not be 100% correct, but this is my understanding. Feel free to comment if improvements can be made.

Upvotes: 1

Related Questions