discord.js-modules icon indicating copy to clipboard operation
discord.js-modules copied to clipboard

async/await strange undefined behavior

Open ryanpag3 opened this issue 3 years ago • 6 comments

Issue description

When I deploy the following code using async/await my application exits immediately with no error code intermittently (usually 1 of every 4 or 5 tries).

Code sample

export async function deploy() {
    logger.debug(`deploying slash commands`);
    try {
        if (process.env.NODE_ENV !== 'production') {
              // register with test server
              await rest.put(
                  Routes.applicationGuildCommands(process.env.DISCORD_APPLICATION_ID as string, process.env.TEST_GUILD_ID as string),
                  {
                      body: mappedCommands
                  }
              )
        } else {
            // register globally in production
            await rest.put(
                Routes.applicationCommands(process.env.DISCORD_APPLICATION_ID as string),
                {
                    body: mappedCommands
                }
            );
        }
        logger.debug(`slash commands registered`);
    } catch (e) {
        logger.error(`Failed to deploy slash commands.`, e);
        throw e;
    }
}

discord.js version

13.13.1

Node.js version

v16.13.0

Operating system

MacOS

Priority this issue should have

Medium (should be fixed soon)

Which partials do you have configured?

No Partials

Which gateway intents are you subscribing to?

GUILDS

ryanpag3 avatar Dec 24 '21 16:12 ryanpag3

I don't see how this is an issue with the rest module, but it might be with your code. Please make a minimum reproduction sample, and report back if the issue persists!

vladfrangu avatar Dec 24 '21 17:12 vladfrangu

Hiya @vladfrangu, I think this may be a minimum reproduction sample:

import { setTimeout } from "node:timers/promises";
import { Routes } from "discord-api-types/v9";
import { REST } from "@discordjs/rest";

class test {
  REST = new REST().setToken(process.env.DISCORD_TOKEN ?? "");
  async deleteMessages(channelId, messageIds) {
    for (const messageId of messageIds) {
      await this.REST.delete(Routes.channelMessage(channelId, messageId)).catch(() => null); // Ignore errors
      // await setTimeout(1000);
    }
  }
};

const testing = new test();
const messageIds = []; // Put message ids here
const output = await testing.deleteMessages("channel_id", messageIds);
console.log(output);

Populate the messageIds array with message ids to be deleted (put at least 5 in). After running this script, you'll find that the script terminates after about a second with having only deleted about 2-3 messages. Put 50 message ids in, and the result will be the same.

If the process is kept alive, all message ids will be deleted. Additionally, uncommenting the await setTimeout(1000) will allow for all messages to be deleted. It seems for some reason the requests just drop and the process finishes despite the messages not having been deleted yet.

Also, console.log(output) never outputs anything unless the process is kept alive enough for all messages to be deleted.

Jiralite avatar Dec 26 '21 15:12 Jiralite

Thanks for that code sample @Jiralite that is basically the behavior I’m seeing as well with command deployment. async/await doesn’t seem to work right with this module.

ryanpag3 avatar Dec 26 '21 16:12 ryanpag3

This is because timers are unref'd:

https://github.com/discordjs/discord.js-modules/blob/90704400d2ff8043993d59fcb02e9c50eb5a8227/packages/rest/src/lib/handlers/SequentialHandler.ts#L255

Since the sleep is done with ref: false, the event loop does not wait for the timeout to finish, albeit it's an interesting issue here, since it should at least wait because await can keep a process up as long as it's running in the main loop.

kyranet avatar Dec 26 '21 18:12 kyranet

It's also weird that using .then/.catch would solve the issue because await uses the Promise prototype methods under the hood.

KhafraDev avatar Jan 05 '22 18:01 KhafraDev

It's also weird that using .then/.catch would solve the issue because await uses the Promise prototype methods under the hood.

I did some more testing on this and I don't think I was accurate about that. They had similar issues.

ryanpag3 avatar Jan 05 '22 18:01 ryanpag3