如何在异步函数返回之前完成等待的循环

时间:2019-01-23 17:56:04

标签: javascript node.js async-await

我正在构建Discord音乐机器人,并且需要使用此功能生成一个对象。问题在于该函数返回得太早并且该对象未完全构建。功能中的循环仅在退出功能后结束。

功能fetchVideoInfo().then()所承诺的承诺功能中执行传递的回调,如镜像所提供。但是我不能编辑它,因为它是模块的一部分。我认为这是一个问题,即使我等待fetchVideoInfo()完成,它仍然会因为它的写法而继续进行,并在内部promise之后执行回调。病态的部分代码返回了实际的承诺并调用了回调函数。我试过的是让我的函数返回诺言,但是对于我认为相同的问题却没有奏效,函数在回调之前就必须等待。我还尝试了将回调函数包装到另一个函数中,然后在等待初始函数的同时传递它,但效果也不佳。

https://imgur.com/HdFF8VJ这是该模块中返回值图像的链接(“ youtube-info”)。整个模块实际上是fetchVideoInfo()函数

async function generatePlayList(queue) {
  const date = new Date();
  let embed = new Discord.RichEmbed();
  embed
    .setTitle("Playlist")
    .setColor("#25473A")
    .setDescription("Music currently in playlist!")
    .setFooter("Time ")
    .setTimestamp(date);
  for await (let id of queue)
    fetchVideoInfo(id, (Null, info) => {
      const { duration, title, url } = info;
      const seconds = duration % 60;
      const minutes = Math.trunc(duration / 60);
      embed.addField(`[${title}](${url})`, `Duration ${minutes}:${seconds}`);
      console.log(embed.fields);
    });

  console.log(embed.fields);
  return embed;
}

现在发生的事情是,该函数首先返回,导致embed对象未经修改,即使在该对象之前有for-await-循环(这是ES2018的新语法)。它应该首先完成for-of循环,然后返回

2 个答案:

答案 0 :(得分:0)

Await不适用于回调,您必须更改函数,并且不应返回回调,而应返回值并使用它,并在出错时抛出错误并捕获它。 for是异步运行的,因此需要等待您的功能。

async function generatePlayList(queue) {
  const date = new Date();
  let embed = new Discord.RichEmbed();
  embed
    .setTitle("Playlist")
    .setColor("#25473A")
    .setDescription("Music currently in playlist!")
    .setFooter("Time ")
    .setTimestamp(date);
  for(let id of queue)
   {
    try{
    const info = await fetchVideoInfo(id);
    const { duration, title, url } = info;
    const seconds = duration % 60;
    const minutes = Math.trunc(duration / 60);
    embed.addField(`[${title}](${url})`, `Duration ${minutes}:${seconds}`);
    console.log(embed.fields);
    }catch(err){
       console.log("Error occur in embed");
     }
    }

  console.log(embed.fields);
  return embed;
}

答案 1 :(得分:0)

所以这里有一个答案,但是它在3分钟内被删除了,但我仍然设法尝试了一下,并且有效!因此,我将其发布在这里,希望它会有用!

async function generatePlayList() {
  const queue = this.queue;
  const date = new Date();
  let embed = new Discord.RichEmbed();
  embed
    .setTitle("Playlist")
    .setColor("#25473A")
    .setDescription("Music currently in playlist!")
    .setAuthor(this.bot.user.name, this.bot.user.avatarURL)
    .setFooter("Time ")
    .setTimestamp(date);

  let promises = queue.map(id => {
    return new Promise((resolve, reject) => {
      fetchVideoInfo(id, (err, info) => {
        if (err) return reject(err);
        const { duration, title, url } = info;
        const seconds = duration % 60;
        const minutes = Math.trunc(duration / 60);
        const fieldTitle = `${title.replace(/ *\([^)]*\) */g, "")}`;
        embed.addField(fieldTitle, `Duration ${minutes}:${seconds}`);
        resolve(embed);
      });
    });
  });

  return Promise.all(promises).then(values => {
    return values[values.length - 1];
  });
}

我在这里所做的是创建一个数组promises,其中每个我必须添加到该对象的字段都包含一个Promise。每个承诺都在fetchVideoInfo的回调函数中解析,因此它必须完成回调才能解析。然后,我返回Promise.all,它解析每个Promise数组promises并返回values数组。由于最后一个解析的Promise是包含最新对象的对象,因此我用values[values.length - 1]对其进行了选择并返回。 Promise.all,将返回从then回调函数返回的值。

当我调用该函数时,我会这样调用它:

generatePlayList(array).then((embed)=>{
    //Do whatever
})

或者这样:

async function stuf(){
   let embed = await generatePlayList(array);
   //Do whatever
}