0

Discord's 2000 character limit is the most unwelcome restriction, judging from its user feedback, so I have to break up my message in chunks, and send them one by one. However, I'm blocked at the last step.

My sendMessage calls the Discord's send method:

function sendMessage(chanID, msg) {
  return discordbot.channels.cache.get(chanID).send(msg)
}

And I've already broken down the long text into an array called part. This is how I'm trying to send them:

  if (msg.length < 2000) {
    return sendMessage(chanID, msg)
  }

  . . . 

  parts.map(async function(part) {
    return await sendMessage(chanID, part)
  })

The first part is working but obviously the last part is not, as my function should return Discord's send method eventually, which is a promise, and the last loop is not.

However, I don't know nodejs/promise well enough to fix it myself. Adapting from the answer from https://stackoverflow.com/a/24985483/2125837, here is my take:

function sendAll(chanID, parts) {
    return parts.reduce(function(promise, msg) {
        return promise.then(function() {
            sendMessage(chanID, msg)
        });
    }, Promise.resolve());
}

Please help.

xpt
  • 20,363
  • 37
  • 127
  • 216
  • did you try using `const messages = await Promise.all(parts.map(m => sendMessage(chanID, part))`? Old discord.js had a way – Andromeda Feb 24 '22 at 18:26
  • The map function generates the promises to send the messages, but the code seems to terminate before they can fully execute and resolve. Wrap the map in a Promise.all and await it. – kevintechie Feb 24 '22 at 18:26
  • Also you don’t need to await a returned promise. – kevintechie Feb 24 '22 at 18:28
  • Thanks for comments everyone. I do await for each message chunk sent because I'm afraid otherwise they might be sent out of the order. Are the sent order guaranteed in Promise.all? I can try what @Andromeda suggested, but how about my wrapper needs to return Discord's send method eventually (see my first return case)? – xpt Feb 24 '22 at 18:34
  • Can you show your source for `sendMessage()` – Elitezen Feb 24 '22 at 18:35
  • Try a standard `for/for await of` loop – Elitezen Feb 24 '22 at 18:44
  • Using Promise.all could potentially post the message chunks out of order. In which case you can use reduce rather than map. See the highest ranked answer to this question for details https://stackoverflow.com/questions/24660096/correct-way-to-write-loops-for-promise – kevintechie Feb 24 '22 at 18:45
  • Returning an awaited promise doesn't guarantee execution order. It is simply redundant. Also, map, while it will execute in order, doesn't guarantee the order of promise resolution. Therefore, the Discord API could post the messages out of order based on internal race conditions. You actually need to wait until the promise is resolved before executing the next promise. Hence, the need for reduce or 'for of' loops as mentioned by Elitezen. – kevintechie Feb 24 '22 at 19:00
  • Thanks again for everyone's comments. I am not a node/js developer and I don't know it well enough to understand what you are trying to say. I've update the OP with the code to the best of my understanding, and will try to read more... – xpt Feb 24 '22 at 19:08
  • You don't want to send an individual part to the sendAll(...) function. You should send all the parts. The ??? section is where you return the sendMessage(...) promise. – kevintechie Feb 24 '22 at 19:12
  • Gotya @kevintechie, better this time? all good now? – xpt Feb 24 '22 at 19:16
  • Yes. It seems to match my answer. – kevintechie Feb 24 '22 at 19:19
  • Unless you `return` a promise from the `promise.then(...)` callback, there's no real value in using the `.reduce()` pattern over the original `.map()`. – Roamer-1888 Feb 24 '22 at 20:54

1 Answers1

0

If you replace your map function with the following, it will likely work. I haven't actually tested the code.

parts.reduce(function(promise, part) {
  return promise.then(function() {
    return sendMessage(chanID, part)
  });
}, Promise.resolve());
kevintechie
  • 1,441
  • 1
  • 13
  • 15
  • How about using `Promise.all` instead? – Andromeda Feb 24 '22 at 20:24
  • I am not a node/js developer and I don't know it well enough to make your code works for my case. But following Elitezen's _"Try a standard for/for await of loop"_ advice, I was able to make my OP working by simply replace the map() with a standard for loop, with the help code from https://stackoverflow.com/a/37576787/2125837. The for await of loop however, gave me Unexpected reserved word error. – xpt Feb 24 '22 at 21:29
  • for await ... of loops are only available in async functions in version 10.0.0 or later or without await in versions of node that support top-level await – kevintechie Feb 24 '22 at 21:59
  • Some coding standards and linter configs (airbnb) don't permit using await in for loops. – kevintechie Feb 24 '22 at 22:05