如何等待此功能完成?

时间:2020-04-29 21:33:54

标签: javascript node.js promise discord.js

在将数据发送到控制台之前,我需要等待映射功能完成。我知道这与Promise有关。我已经尝试了几个小时,即使阅读了很多关于Promise和异步函数的信息,我也无法使它工作...

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 个答案:

答案 0 :(得分:3)

您正在接近,但距离那里还不远。

首先,一些注意事项:

  • await可以用于任何值,但是将其用于任何非Promise都是毫无意义的。您的guildMembers.map(...);返回一个数组,而不是一个Promise。
  • 混合await.then(...)可以,但是有点混乱。您已经在使用await-为什么还要处理回调?
  • 像这样使用guildMembers.map(async ...)将确保所有请求或多或少地立即触发,并且它们可以按任何顺序完成。很好,但这是一种竞争条件,结果或多或少是随机的。
  • 即使从概念上讲,这也不是一个好方法!每当您不得不循环查询时,请尝试研究仅在一个查询中进行查询的方法。 SQL非常强大。

当前代码不起作用的原因是因为您的connection.query函数转义了异步控制流。我的意思是,使用async / await和Promises的全部目的基本上是在本地跟踪回调,并利用promise链动态添加回调。如果您调用返回Promise的异步函数,则现在可以将该Promise携带到代码中的其他任何位置,并动态地将成功处理程序附加到该处理程序:使用.then()或使用糖await

但是connection.query函数不会返回Promise,它只是传递了另一个裸回调-此Promise不会跟踪该回调! Promise没有对该回调的引用,它不知道何时调用该回调,因此您的async / await控制流得以转义,并且您的promise在查询运行很久之前就解决了。

您可以通过在异步函数中创建一个新的Promise来解决此问题:

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 = 0lastMSGDate BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW()。这样,您就可以将所有当前的处理逻辑卸载到SQL Server上-您应该尝试每次都可以做到。它将大大简化此代码。我不会进一步介绍这个问题,因为它超出了这个问题的范围,但是如果您无法解决,请随时询问另一个问题。

答案 1 :(得分:1)

我无法测试,但这对我来说通常是想等待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();