【问题标题】:How to have awaited loop complete before return in async function如何在异步函数中返回之前完成等待循环
【发布时间】:2019-06-17 09:14:36
【问题描述】:

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

函数fetchVideoInfo() 执行在.then() 函数中传递的回调,它返回的promise,如图所示。但是我无法编辑它,因为它是模块的一部分。我认为这是一个问题,即使我在等待fetchVideoInfo() 完成,它仍然会继续,因为它是如何编写的,在内部承诺之后执行回调。我将提供返回实际承诺并调用回调函数的部分代码。我尝试过让我的函数返回承诺,但它不适用于我认为相同的问题,函数在回调之前结束,我必须等待。我也尝试过将回调函数包装在另一个函数中然后传递它,同时等待初始函数,但它也做得不好。

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 - of 循环(这是新的 ES2018 语法)。它应该先完成for-of循环然后返回

【问题讨论】:

  • await 只对promise 生效,即使函数是async 如果等待的方法不是promise 也不会产生wait 效果。简而言之,您必须将填充对象的方法转换为promise
  • 你能举个例子吗?我尝试了多种方式,但没有达到我的预期

标签: javascript node.js async-await


【解决方案1】:

所以这里有一个答案,但它在 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,用于我必须添加到对象的每个字段。每个 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
}

【讨论】:

    【解决方案2】:

    Await 不适用于必须更改函数的回调,并且不应返回回调,而是返回一个值并使用它,并在错误时抛出错误并捕获它。 for of 异步运行,所以你的函数需要等待。

    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;
    }
    

    【讨论】:

    • 但是 fetchVideoInfo() 将结果传递给回调函数,我无法更改。它返回一个执行回调函数的承诺,请参阅我提供的链接。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多