【问题标题】:Discord.js awaitReaction chained promise after message.react collect the reaction as promise isn't resolvedDiscord.js awaitReaction 在 message.react 之后链式承诺收集反应,因为承诺未解决
【发布时间】:2019-12-04 01:34:55
【问题描述】:

我正在玩 message.react 函数和 awaitReactions 函数,但我注意到了一些困扰我的事情。

我试图确定是否应该对以下方法使用收集器或client.on('messageReactionAdd')(这是另一个问题):

  1. 发送消息
  2. 为其添加反应
  3. 每次添加反应时都做某事(持续 X 秒)

所以我做了一个简单的例子开始,过滤器每次都返回true,我注意到收集器正在收集我的机器人添加到消息中的最后一个表情符号。这是代码

const emojiNext = '➡';
const emojiPrevious = '⬅';
const emojiClap = '????';

function filter(reaction) {
  console.log('reacted to: ', reaction.emoji.name);
  return true;
}
function sleep(ms, a){
  return new Promise(resolve=>{
    setTimeout(resolve,ms);
  }).then(d => a);
}

function sendList(channel){
  channel.send('foo')
  .then(msg => {
    console.log('First');
    return msg.react(emojiPrevious);
  })
  .then(msgReaction => {
    console.log('Second', msgReaction.message.reactions.keys());
    return msgReaction.message.react(emojiNext);
  })
  .then(msgReaction => {
    console.log('Third', msgReaction.message.reactions.keys());
    return msgReaction.message.react(emojiClap);
  })
  // .then(msgReaction =>{
  //   return sleep(100, msgReaction);
  // })
  .then(msgReaction => {
    console.log('Fourth',  msgReaction.message.reactions.keys());
    msgReaction.message.awaitReactions(filter, {max: 2, time: 1000, errors: ['time']})
    .then(collected => {
      console.log('ending', collected);
    })
    .catch(collected => {
      console.log(`After 5 sec, only ${collected.size} out of 2 reacted: ${collected.map((v,k) => k)}`);
    });
  });
}

调试

这个例子比我做的第一个例子要复杂一些,因为我尝试了一些调试。

示例发送一条消息,附加链式 promise 3 表情符号,然后开始收集表情符号。 但是,如以下日志所示,最后一个表情符号被收集(我自己从未按过任何表情符号,而且我一个人在我的服务器上):

First
Second [Map Iterator] { '⬅' }
Third [Map Iterator] { '⬅', '➡' }
Fourth [Map Iterator] { '⬅', '➡', '????' }
reacted to:  ????
After 5 sec, only 1 out of 2 reacted: ????

有时(视情况而定)它工作正常,日志是这样的:

First
Second [Map Iterator] { '⬅' }
Third [Map Iterator] { '⬅', '➡' }
Fourth [Map Iterator] { '⬅', '➡', '????' }
After 5 sec, only 0 out of 2 reacted: 

我最后在火车上进行测试,可能是由于网络不规则。

所以我尝试了其他方法,我添加了 sleep 函数,它接受一个 ms 和一个值,并返回一个 promise,它会在 ms 量过去后返回值。 (取消注释 3 行以进行此调试)。 这样收集器就不会收集到最后一个表情符号。

我还尝试使用收集器 (let res = await channel.send...) 在 then 之前返回承诺,然后执行其余代码。我仍然收集了最后一个表情符号。

我知道我可以使用我的过滤器来忽略机器人或我的表情符号,而只关注用户的(见下面的代码),但我想知道是什么导致了这种行为。是不是我做错了什么?关于 promise 有什么我不明白的地方吗?

function filter(reaction, user) {
  if(user.id === client.user.id) { return false; } // or user.bot to ignore all bot
  console.log('reacted to: ', reaction.emoji.name);
  return true;
}

在我看来,在调试中看到收集器在记录所有内容后做出反应后,我认为解决的承诺和 Discord 将信息发送到收集器/回调是不同的,但这只是一个猜测

注意:
node.js 版本:v11.15.0
discord.js 版本:v11.5.1

【问题讨论】:

  • 看起来像是某种不受你控制的种族效应......或者只是机器中的幽灵。

标签: node.js promise discord discord.js


【解决方案1】:

这是由于与 Discord 交互时 REST APIGateway 之间的分离。

在后端,message.react 导致 discord.js 触发对 Discord 的 REST API 的请求 - Discord 将接收此请求、处理它并返回响应,这导致 message.react 返回的承诺要么解决或拒绝。

但是,当您监听反应时,它根本不是在监听 REST API,而是在监听 网关事件 - 这些主要是导致像 client.on('messageReactionAdd') 这样的事件在客户。

当您收到来自 discord 的回复说您的反应已成功发布时,discord 不一定已完成向所有客户(包括您的机器人)发送 messageReactionAdd 事件以进行反应!它可能有,但可能没有。然而,在收到 API 响应后,您会立即开始监听来自网关的响应。这就是所谓的竞争条件 - Discord 负责 REST API 的服务器正在与负责网关的服务器竞争,谁会先将这条信息发送给你的机器人。 p>

这不是您可以合理避免的事情,因为您绝对无法知道网关何时会赶上事件并向您的机器人发送自己的反应 - 所以您应该在这里做的是考虑竞争条件您的过滤器,正如您在问题中所承认的那样。另一种解决方案可能涉及添加一个反应事件侦听器,并且仅在侦听器检测到您的机器人的反应已通过网关后才开始侦听新的反应,但这对于这种特定情况来说不太干净。


顺便说一句,我强烈建议使用async/await,而不是那些丑陋和不可读的承诺链。您的代码可以简化为:

async function sendList(channel){
  let msg = await channel.send('foo');
  console.log('First');
  let msgReaction = await msg.react(emojiPrevious);
  console.log('Second', msgReaction.message.reactions.keys());
  msgReaction = await msg.react(emojiNext);
  console.log('Third', msgReaction.message.reactions.keys());
  msgReaction = await msg.react(emojiClap);
  console.log('Fourth',  msgReaction.message.reactions.keys());
  try {
    let collected = await msgReaction.message.awaitReactions(filter, {max: 2, time: 1000, errors: ['time']})
    console.log('ending', collected);
  } catch(partialCollection) {
    console.log(`After 5 sec, only ${partialCollection.size} out of 2 reacted: ${partialCollection.map((v,k) => k)}`);
  }
}

应该与您当前的代码完全相同,但在我看来更具可读性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 2015-06-24
    • 2020-03-16
    • 2019-04-13
    • 2016-01-10
    • 1970-01-01
    相关资源
    最近更新 更多