【问题标题】:Why even after using async await for multiple calls still got empty response?为什么即使在对多个调用使用 async await 之后仍然得到空响应?
【发布时间】:2019-03-03 05:34:31
【问题描述】:

这是调用异步api 的简单节点路由。

循环后需要return data。但它返回的是空白对象。

try {
  const array = ["brunch", "lunch", "crunch"]
  const data = {}
  array.map(async(d) => {
    const venue = await Venue.find({ "category": { "$in": [d] }})
    data[d] = venue
  })
  return data
} catch(err) {
  throw err
}

请帮助我实现这一目标

【问题讨论】:

  • @T.J.Crowder 如何将动态键也放入我的数据对象?我不这么认为它是重复的
  • this fiddle有帮助
  • 因为 array.map 返回一个 promises 数组 - 你需要等待它们全部完成......注意,你的代码(和小提琴)不会连续执行Venue.find,它并行执行所有这些-如果需要一个接一个(某些API不喜欢一次多个请求),那么您将使用不同的代码-即this fiddle
  • @T.J.Crowder 已打开,因为该问题有其他解决方案
  • @DarkKnight - 我愿意,当我认为有任何真正的机会需要澄清时。 (有时相当广泛的 cmets。)我没有(也没有)在这里。我确实努力成为一个相当有用的人。 :-)

标签: javascript node.js mongodb async-await aggregation-framework


【解决方案1】:

有一种更好的方法可以使用 MongoDB 获得所需的结果并且无需循环,使用聚合框架,您可以在其中运行以下管道,该管道使用 $facet 作为

try {
    const array = ["brunch", "lunch", "crunch"]
    const facet = array.reduce((acc, cur) => {
        acc[cur] = [{ "$match": { "category": cur } }]
        return acc
    }, {})
    const pipeline = [
        { "$match": { "category": { "$in": array } } },
        { "$facet": facet }
    ]
    const results = await Venue.aggregate(pipeline).exec()
    const data = results[0]

    return data
} catch(err) {
    throw err
}

您还可以按类别键和 $push 将文档分组,然后将 $replaceRoot 中的文档转换为文档的键 $replaceRoot强>$arrayToObject

try {
    const array = ["brunch", "lunch", "crunch"]
    const pipeline = [
        { "$match": { "category": { "$in": array } } },
        { "$group": { 
            "_id": "$category",
            "data": { "$push": "$$ROOT" }
        } },
        { "$group": {
            "_id": null,
            "venues": {
                "$push": {
                    "k": "$_id",
                    "v": "$data"
                }
            } 
        } },
        { "$replaceRoot": {
            "newRoot": { "$arrayToObject": "$venues" }
        } }
    ]
    const results = await Venue.aggregate(pipeline).exec()
    const data = results[0]

    return data
} catch(err) {
    throw err
}

【讨论】:

  • 感谢 chridam 的回答。但我知道这个技巧。 stackoverflow.com/questions/50735135/…在 $group 阶段不可能将“数百万个文档”$push 到一个数组中这个评论吓到我了。
  • 任何一种方法都有效,这是聚合框架的限制;将文档推送到数组时突破 16MB 限制。唯一的性能收获是您只是对服务器进行一次调用,但由于您正在迭代一个小数组,因此在使用 for 循环时性能成本可以忽略不计,并且建议在@RaghavGarg 的另一个答案中使用该方法跨度>
  • 非常感谢您的回答。
  • 我没有看到您使用$facet 的第一个答案。那很完美!!!再次感谢您
【解决方案2】:

虽然@chridam 方法非常独特并且可能更有效,以防你想坚持使用循环。

有两种方法。您希望所有操作并行或串行运行。

如果并行,则必须使用Promise.all

try {
  const array = ["brunch", "lunch", "crunch"]
  const data = {}
  await Promise.all(array.map(async(d) => {
    data[d] = await Venue.find({ "category": { "$in": [d] }})
  }))
  return data
} catch(err) {
  throw err
}

如果是系列,则必须使用简单的for 循环。

array.map(async(d) => {}) 正在使内部数据库调用异步并且不等待操作。正常的for 循环将是同步的。

try {
  const array = ["brunch", "lunch", "crunch"]
  const data = {}
  for (d of array) {
    data[d] = await Venue.find({ "category": { "$in": [d] }})
  }
  return data
} catch(err) {
  throw err
}

【讨论】:

  • 你的并行代码返回一个数组而不是一个对象 - 所以,根本不是答案
  • 或者你可以只用 map 做的更整洁,而不是在一个循环中减少 - jsfiddle.net/kqsa3upb :p
  • @JaromandaX,再次感谢,伙计。从来没有想过这样做。
【解决方案3】:

问题在于您使用async-await 的方式。它在Array.prototype.map 内部,它使.map 函数异步,因此主线程永远不会等待循环完成并前进到下一条语句并返回data,即{}

try {
  const array = ["brunch", "lunch", "crunch"]
  const data = {};

  // here's the issue async is callback function to map, due to which never waited for map function to finished.
  array.map( async(d) => {
    const venue = await Venue.find({ "category": { "$in": [d] }})
    data[d] = venue;
  });

  return data;
} catch(err) {
  throw err
}

把代码改成这样:

(async function () {
    try {
      const array = ["brunch", "lunch", "crunch"]
      const data = {};

      array.map( d => {
        const venue = await Venue.find({ "category": { "$in": [d] }})
        data[d] = venue;
      });

      return data;

    }
    catch(err) {
      throw err
    }
})();

你所做的与此类似

function main () {
    return new Promise ( resolve => {
        setTimeout( () => {
            console.log("First");
            resolve(true);
        },5000 );
    });
}

(async function () {
    await main();

    console.log("Second")
})();

console.log("Third");
// Third
// First
// Second

【讨论】:

  • 谢谢你的回答
  • 这段代码是完全错误的......在一个不是的函数中使用async将无法解析,更不用说运行了!!!在您认为将 array.map( d => { 更改为 array.map( await d => { 会以某种方式解决问题之前,请考虑这会使您的代码与问题中的代码相同
  • 没有我要求以其他方式将 array.map( await d => { 更改为 array.map( d => {
猜你喜欢
  • 1970-01-01
  • 2019-04-22
  • 2020-10-02
  • 2016-12-13
  • 1970-01-01
  • 2016-06-13
  • 1970-01-01
  • 2018-09-26
  • 2019-10-22
相关资源
最近更新 更多