0

In short, I am making a Discord bot with discord.js and I am having trouble with asynchronous and synchronous functions. In order to assign variables, the last part of the function loops through each variable and then converts it to its desired type, which can be seen here:

      argsList.forEach((argument, index) => {
            let finalArgument = argument

            const type = args[index].type
            if (type === UserArgument) {
                new UserArgument(argument).result
                .then(userObject => {
                    finalArgument = userObject
                })
                .catch(error => {
                    throw error
                })
            } else if (type === MemberArgument) {
                new MemberArgument(argument, guild).result
                .then(memberObject => {
                    finalArgument = memberObject
                })
                .catch(error => {
                    throw error
                })
            } else if (type === ChannelArgument) {
                new ChannelArgument(argument, guild).result
                .then(channelObject => {
                    finalArgument = channelObject
                })
                .catch(error => {
                    throw error
                })
            } else if (type === RoleArgument) {
                new RoleArgument(argument, guild).result
                .then(roleObject => {
                    finalArgument = roleObject
                })
                .catch(error => {
                    throw error
                })
            }

            finalArgList.push(finalArgument)
        })
        return finalArgList
      }

And here is an example of how the UserArgument class looks like (all other argument bases basically look the same)

class UserArgument {
    constructor(user) {
        this.result = new Promise((resolve, reject) => {
            if (typeof(user) === "string") {
                if (user.match(/^<@!([0-9]+)>$/)) {
                    user = user.match(/[0-9]+/)
                }
                if (isNumeric(user)) {
                    ArgumentBase.client.users.fetch(user)
                    .then(userObject => {
                        resolve(userObject)
                        return userObject
                    })
                }
                this.#getUserFromName(user)
                .then(userObject => {
                    resolve(userObject)
                    return userObject
                })
            } else if (user instanceof DiscordJS.User) {
                resolve(user)
                return user
            } else if (user instanceof DiscordJS.GuildMember || user instanceof DiscordJS.ThreadMember) {
                resolve(user.user)
                return user.user
            }
            let userObject = ArgumentBase.client.users.resolve(user)
            if (userObject) {
                resolve(userObject)
                return userObject
            }
            reject(new UserNotFound(toString(user)))
        })
    }

    async #getUserFromName(username) {
        return new Promise((resolve, reject) => {
            for (const user in ArgumentBase.client.users.cache) {
                if (user.username === username) {
                    resolve(user)
                    return user
                }
            }
            throw new UserNotFound(username)
        })
    }
}

The issue that I am coming across is that the code that handles each argument does not wait for the function to be finished and instead skips over it. This of course causes the command to be executed before the arguments are even processed. For my tests, I was testing throwing errors from the UserArgument class, and the error did get thrown, but only after the command had already executed because that is when it decided to finish.

My assumption is that since Promise is an asynchronous function, the code keeps running and does not wait for it. I tried turning the argument function to an async function and use await, but I kept getting the SyntaxError: await is only valid in async functions and the top level bodies of modules, even when the function is an async function (function declaration is static async getCommandArgs(invokedString, args, guild = undefined)). If someone could help me, that would be amazing. Thank you for your time and help.

Batimius
  • 99
  • 3
  • 10
  • Does this answer your question? https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – Christian Sep 23 '21 at 08:25
  • @Christian , Sadly not. I am not really trying to run the loop asynchronously, I am just trying to get the calls to the function to be waited for instead of skipped over. In short, I want to turn Promise into a synchronous function, per say. I know that this can be done with await, but it seems as if node does not want me to use await for some reason as I keep getting the `SyntaxError: await is only valid in async functions and the top level bodies of modules` error, even in an async function. – Batimius Sep 23 '21 at 08:40
  • 2
    I would start by moving out the logic from the constructor. You should never use async calls in a constructor. – Christian Sep 23 '21 at 09:57

0 Answers0