Как дождаться окончания этой функции до 1000 *? - PullRequest
0 голосов
/ 30 апреля 2020

Мне нужно дождаться окончания функции отображения до sh, прежде чем я отправлю данные на консоль. Я знаю, что это как-то связано с Promise. Я пытался часами, и я не мог заставить его работать, даже после прочтения так много об обещаниях и asyn c функциях ...

async function inactiveMemberWarner() {
    var msg = "```javascript\nI have sent warnings to members that have been inactive for 2 weeks.\n\n"
    var inactiveMembers = '';
    var count = 0;
    var guildMembers = client.guilds.find(g => g.name === mainGuild).members;

    const keyPromises = await guildMembers.map(async (member) => {
        if (isMod(member)) {
            connection.query(`SELECT * from users WHERE userID='${member.id}'`, (err, data) => {
                if (data[0]) {
                    if (!data[0].warnedForInactivity && moment().isSameOrAfter(moment(data[0].lastMSGDate).add('2', 'week'))) {
                        count++;
                        var updateWarning = {warnedForInactivity: 1}
                        connection.query(`UPDATE users SET ? WHERE userID='${data[0].userID}'`, updateWarning);
                        member.send(`**[*]** WARNING: You've been inactive on \`\`${mainGuild}\`\` for 2 weeks. Members that have been inactive for at least a month will be kicked.`);
                        inactiveMembers += `${count}. ${member.user.tag}\n`;
                        return inactiveMembers;
                    }
                }
            });
        }
    });

    await Promise.all(keyPromises).then(inactiveMembersData => console.log(inactiveMembers)); // RETURNS AN EMPTY STRING

    setTimeout(() => console.log(inactiveMembers), 5000); // RETURNS THE INACTIVE MEMBERS AFTER WAITING FOR 5 SECONDS (PRMITIVE WAY)
}

inactiveMemberWarner();

Заранее спасибо!

Ответы [ 2 ]

3 голосов
/ 30 апреля 2020

Вы близки, но не совсем там.

Во-первых, некоторые примечания:

  • await можно использовать для любого значения, но использовать его совершенно бессмысленно это на что-нибудь, что не обещание. Ваш guildMembers.map(...); возвращает массив , а не Promise.
  • Смешивание await и .then(...) работает, но это немного грязно. Вы уже используете await - зачем беспокоиться о обратных вызовах?
  • Использование guildMembers.map(async ...), как это, гарантирует, что все запросы выполняются более или менее мгновенно, и они могут завершить sh в любом порядке , Это хорошо, но это своего рода состояние гонки и приводит к более или менее случайному порядку результатов.
  • Это не очень хороший подход даже просто концептуально! Каждый раз, когда вам нужно выполнить l oop запросов, попробуйте найти способы сделать это только в одном запросе. SQL довольно мощный.

Причина, по которой ваш текущий код не работает, заключается в том, что ваша connection.query функция выходит из асинхронного c потока управления . Под этим я подразумеваю, что весь смысл использования async / await и Promises заключается в основном в локальном отслеживании обратных вызовов и использовании цепочки обещаний для динамического добавления обратных вызовов. Если вы вызываете асинхронную функцию c, которая возвращает Обещание, теперь вы можете выполнять это Обещание в любом месте вашего кода и динамически присоединять к нему обработчик успеха: либо с .then(), либо с сахаром await.

Но функция connection.query не возвращает Обещание, она просто заставляет вас пройти еще один голый обратный вызов - этот Обещание не отслеживается Обещанием! Обещание не имеет ссылки на этот обратный вызов, оно не может знать, когда вызывается этот обратный вызов, и, следовательно, ваш поток управления асинхронным / ожидающим завершается, и ваши обещания разрешаются задолго до выполнения запросов.

Вы можете решить эту проблему, выполнив новое обещание в функции asyn c:

async function inactiveMemberWarner() {
    var msg = "```javascript\nI have sent warnings to members that have been inactive for 2 weeks.\n\n"
    var inactiveMembers = '';
    var count = 0;
    var guildMembers = client.guilds.find(g => g.name === mainGuild).members;

    const keyPromises = guildMembers.map(async (member) => {
        if (isMod(member)) {
            return new Promise((resolve, reject) => {
                connection.query(`SELECT * from users WHERE userID='${member.id}'`, (err, data) => {
                    if (err) reject(err); //make errors bubble up so they can be handled
                    if (data[0]) {
                        if (!data[0].warnedForInactivity && moment().isSameOrAfter(moment(data[0].lastMSGDate).add('2', 'week'))) {
                            count++;
                            var updateWarning = {warnedForInactivity: 1}
                            connection.query(`UPDATE users SET ? WHERE userID='${data[0].userID}'`, updateWarning);
                            member.send(`**[*]** WARNING: You've been inactive on \`\`${mainGuild}\`\` for 2 weeks. Members that have been inactive for at least a month will be kicked.`);
                            resolve(`${count}. ${member.user.tag}\n`;);
                        }
                    } else resolve(""); //make sure to always resolve or the promise may hang
                });
            });
        }
    });

    let inactiveMembersData = await Promise.all(keyPromises); // Returns an array of inactive member snippets.
    inactiveMembers = inactiveMembersData.join(""); //join array of snippets into one string
}

inactiveMemberWarner();

Это будет работать, но есть намного лучший способ . SQL поддерживает оператор IN , который позволяет иметь такие условия, как WHERE userID IN (list_of_ids). Другими словами, вы можете сделать это в одном запросе . Вы даже можете указать больше условий, таких как warnedForInactivity = 0 и lastMSGDate BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW(). Таким образом, вы можете разгрузить все свои текущие логики обработки c на сервер SQL - то, что вы должны пытаться делать виртуально каждый раз, когда вы можете . Это также сильно упростит этот код. Я не буду go дальше, так как это выходит за рамки этого вопроса, но не стесняйтесь спрашивать другого, если вы не можете понять это.

1 голос
/ 30 апреля 2020

Я не могу проверить это, но это то, что обычно работает для меня, когда я хочу ждать по smt:

async function inactiveMemberWarner() {
    new Promise(function(cb,rj){
        var msg = "```javascript\nI have sent warnings to members that have been inactive for 2 weeks.\n\n"
        var inactiveMembers = '';
        var count = 0;
        var guildMembers = client.guilds.find(g => g.name === mainGuild).members;
        const keyPromises = await guildMembers.map(async (member) => {
            if (isMod(member)) {
                connection.query(`SELECT * from users WHERE userID='${member.id}'`, (err, data) => {
                    if (data[0]) {
                        if (!data[0].warnedForInactivity && moment().isSameOrAfter(moment(data[0].lastMSGDate).add('2', 'week'))) {
                            count++;
                            var updateWarning = {warnedForInactivity: 1}
                            connection.query(`UPDATE users SET ? WHERE userID='${data[0].userID}'`, updateWarning);
                            member.send(`**[*]** WARNING: You've been inactive on \`\`${mainGuild}\`\` for 2 weeks. Members that have been inactive for at least a month will be kicked.`);
                            inactiveMembers += `${count}. ${member.user.tag}\n`;
                            cb(inactiveMembers);
                        }
                    }
                });
            }
        });
        cb('No Members');
    }).then(inactiveMembersData => console.log(inactiveMembers)); // SHOULD RETURNS THE INACTIVE MEMBERS
}

inactiveMemberWarner();
...