首页 > 解决方案 > Discord poll bot async issues

问题描述

I am trying to make a poll command for a discord bot in which the user chooses a number of options in the first command (ie '!poll 4') and then chooses the questions and the options. I am having some issues getting the bot to wait for a response before it moves on to the next option in the loop. When I try and use await in the loop it says I cannot use await because it's not an async function, but it is an async function I think. I am very inexperienced with this so I am sure it is a simple error or probably multiple. If anyone can give me advice on a way to make the loop work as intended and ask for each option I would appreciate it. Also is there a way to add if statements to do addFields to an embed? Here is my code:

const Discord = module.require('discord.js');

module.exports = {
    name: 'poll',
    async execute(message, args) {
        function isNumber(n) { return !isNaN(parseFloat(n)) && !isNaN(n - 0) }
        if(isNumber(args[1])){

            if(args[1]<2) return message.channel.send('Please choose a higher number of options for the poll :)');
            if(args[1]>10) return message.channel.send('Please choose a lower number of options for the poll :)');

            const filter = response => {
                if(!response.author.bot) return response;
            };

            var question;
            var options;

            message.channel.send('What question would you like to ask?').then(() => {
                message.channel.awaitMessages(filter, { max: 1, time: 15000})
                .then(collected => {
                    question = `${collected.first()}?`;
                    message.channel.send('Question: ' + question);
                    
                    for (var i = 0; i < args[1]; i++) {
                        message.channel.send('What is option ' + (i + 1) + '?').then(() => {
                            message.channel.awaitMessages(filter, { max: 1, time: 15000})
                            .then(collected => {
                                options[i] = collected.first;
                                message.channel.send(`Option ${i}: ${options[i]}`);
                            })
                            .catch(collected => {
                                message.channel.send('Poll has timed out.');
                            });
                        })
                    }
                })
                .catch(collected => {
                    message.channel.send('Poll has timed out.');
                });
                const embed = new Discord.MessageEmbed()
                .setColor(3447003)
                .setTitle(question)
                .setDescription('choose an option')
                /*
                if (options[0]) .addField('1️⃣:' + option[0])
                if (options[1]) .addField('2️⃣:' + option[1])
                if (options[2]) .addField('3️⃣:' + option[2])
                if (options[3]) .addField('4️⃣:' + option[3])
                if (options[4]) .addField('5️⃣:' + option[4])
                if (options[5]) .addField('6️⃣:' + option[5])
                if (options[6]) .addField('7️⃣:' + option[6])
                if (options[7]) .addField('8️⃣:' + option[7])
                if (options[8]) .addField('9️⃣:' + option[8])
                if (options[9]) .addField(':' + option[9])
                */

                message.channel.send(embed).then(embedMessage => {
                    if (options[0]) embedMessage.react('1️⃣');
                    if (options[1]) embedMessage.react('2️⃣');
                    if (options[2]) embedMessage.react('3️⃣');
                    if (options[3]) embedMessage.react('4️⃣');
                    if (options[4]) embedMessage.react('5️⃣');
                    if (options[5]) embedMessage.react('6️⃣');
                    if (options[6]) embedMessage.react('7️⃣');
                    if (options[7]) embedMessage.react('8️⃣');
                    if (options[8]) embedMessage.react('9️⃣');
                    if (options[9]) embedMessage.react('');
                });
            });
        }
    }
}

标签: discord.js

解决方案


既然你说你试图await在你的循环中使用,让我从你的代码片段中取出它包含的函数,稍微格式化一下,然后尝试做一些解释。免责声明:我不是专家,所以我也在学习。

.then(collected => {
    question = `${collected.first()}?`;
    message.channel.send(`Question: ${question}`);

    for (var i = 0; i < args[1]; i++) {
        message.channel.send(
            `What is option ${i + 1}?`
        ).then(() => {
            message.channel.awaitMessages(filter, {
                "max": 1,
                "time": 15000,
            }).then(collected => {
                options[i] = collected.first;
                message.channel.send(`Option ${i}: ${options[i]}`);
            }).catch(collected => {
                message.channel.send("Poll has timed out.");
            });
        });
    }
});

但在此之前,由于第一个内部.then()仍然返回 a Promise,您可以在外部范围内链接第二个内部.then()以避免嵌套太深,并.catch()在最后留下一个。关于这一点,将 catch 的参数称为error. 所以这是新的片段:

.then(collected => {
    question = `${collected.first()}?`;
    message.channel.send('Question: ' + question);

    for (var i = 0; i < args[1]; i++) {
        message.channel.send(
            `What is option ${i + 1}?`
        ).then(() => {
            message.channel.awaitMessages(filter, {
                "max": 1,
                "time": 15000,
            });
        }).then(collected => { // Change .then() chaining
            options[i] = collected.first;
            message.channel.send(`Option ${i}: ${options[i]}`);
        }).catch(error => { // Change parameter name
            message.channel.send("Poll has timed out.");
        });
    }
})

现在发生的情况是每次迭代都立即一个接一个地运行。你.send()有一大堆消息,每个都返回一个Promise,然后Promise,你传递一个回调函数.then(),一旦每个Promise解析成一个Message. 该回调隐式返回 的结果.awaitMessages(),这也是一个承诺,一旦解决,下一个回调中的下一个回调.then()将使用前一个承诺解决的值作为参数传入,依此类推。

好的,所以您希望整个 Promise 链在进行下一次迭代之前完成处理和解析,对吗?您可以使用await关键字暂停相关匿名函数的进度,直到其相关的基于承诺的操作解决或拒绝。问题是该函数必须用async关键字标记,并且在您的代码中,实际上并非如此,您只是在使用Promises 和回调函数(关于“但我认为它是一个异步函数”)。因此,让我们添加上述两个关键字:

.then(async collected => { // Mark as async
    question = `${collected.first()}?`;
    message.channel.send('Question: ' + question);

    for (var i = 0; i < args[1]; i++) {
        await message.channel.send( // Wait for this entire Promise chain to resolve before proceeding
            `What is option ${i + 1}?`
        ).then(() => {
            message.channel.awaitMessages(filter, {
                "max": 1,
                "time": 15000,
            });
        }).then(collected => {
            options[i] = collected.first;
            message.channel.send(`Option ${i}: ${options[i]}`);
        }).catch(error => {
            message.channel.send("Poll has timed out.");
        });
    }
})

这应该会导致您想要的行为,尽管我的编辑可能有语法错误,因为我自己没有运行它。如果我有什么问题,请发表评论。

你可以在这里阅读更多:


推荐阅读