'Async/Await function executing twice in Discord.js bot

I've been experimenting with a Discord bot in Node.js. I came across the problem of all functions not executing linearly, but asynchronously. I managed to introduce async/await to solve this issue, but now one of my function calls executes twice and I don't know why (The command !ec2-status prints twice on a given chat even though I only call it once). The routine that reacts to a message sent is as follows:

// Event: Message
client.on("messageCreate", msg => {

    // Extract metadata from message
    let msgServerID = msg.guildId;
    let msgChannelID = msg.channelId;
    let msgUserID = msg.author.id;
    let msgUsername = msg.author.username;

    // Select server and channel of current message
    let msgServer = client.guilds.cache.get(msgServerID);
    let msgChannel = msgServer.channels.cache.get(msgChannelID);

    // Command (!status): 
    // Check AWS instance status if server is "Dev Server"
    if (msg.content === "!ec2-status") {
        
        const thingy = async () => {

            let instanceStatuses = await getEC2Status();

            let statusText = "Instance Info: \n";
            for (const instanceID in instanceStatuses) {
                statusText += `${instanceID}: ${instanceStatuses[instanceID]}\n`;
            }

            msg.reply(statusText);
        }

        thingy();
    }
});

The function that gets triggered in order to get the status of my EC2 instances (the objective of the command) is the following:

// Function: Get the status of all EC2 instances
const getEC2Status = async () => {

    // Variable to store the status of all instances
    let instanceStatuses = {};

    // Retrieve instance information without previous permission check (DryRun = False)
    const results = await ec2.describeInstances({ DryRun: false }, (err, data) => {

        // Return an error if an error ocurrs
        if (err) {
            console.log("Retrieve Instance Info: Error\n", err.stack);
        }
        else {

            // Adds the info of each instance
            data.Reservations.forEach(reservation => {
                let instanceID = reservation.Instances[0].InstanceId;
                let status = reservation.Instances[0].State.Name;
                instanceStatuses[instanceID] = status;
            });

            console.log("Retrieve Instance Info: Success\n", instanceStatuses);
        }

    }).promise();

    return instanceStatuses
}

I'm having a hard time wrapping my head around async/await, so that may be the problem. But I need someone to nudge me in the right direction. Right now, I'm a bit stuck.



Solution 1:[1]

Perhaps the following changes help:

  • Make the entire client.on function async, instead of only the single thingy() function you define in there.
  • I'm not sure what the .promise() is doing, but I don't think it makes sense to await something after which you call .promise(). Remove the .promise().

All in all, thus:

// Event: Message
client.on("messageCreate", async (msg) => {

    // Extract metadata from message
    let msgServerID = msg.guildId;
    let msgChannelID = msg.channelId;
    let msgUserID = msg.author.id;
    let msgUsername = msg.author.username;

    // Select server and channel of current message
    let msgServer = client.guilds.cache.get(msgServerID);
    let msgChannel = msgServer.channels.cache.get(msgChannelID);

    // Command (!status): 
    // Check AWS instance status if server is "Dev Server"
    if (msg.content === "!ec2-status") {
        
        let instanceStatuses = await getEC2Status();

        let statusText = "Instance Info: \n";
        for (const instanceID in instanceStatuses) {
            statusText += `${instanceID}: ${instanceStatuses[instanceID]}\n`;
        }

        msg.reply(statusText);
    }
});
// Function: Get the status of all EC2 instances
const getEC2Status = async () => {

    // Variable to store the status of all instances
    let instanceStatuses = {};

    // Retrieve instance information without previous permission check (DryRun = False)
    const results = await ec2.describeInstances({ DryRun: false }, (err, data) => {

        // Return an error if an error ocurrs
        if (err) {
            console.log("Retrieve Instance Info: Error\n", err.stack);
        }
        else {

            // Adds the info of each instance
            data.Reservations.forEach(reservation => {
                let instanceID = reservation.Instances[0].InstanceId;
                let status = reservation.Instances[0].State.Name;
                instanceStatuses[instanceID] = status;
            });

            console.log("Retrieve Instance Info: Success\n", instanceStatuses);
        }

    });

    return instanceStatuses
}

Without more information, I think these are the best first steps.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dharman