为什么即使在使用async等待多个呼叫后仍然得到空响应?

时间:2018-09-27 10:16:54

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

这是简单的节点路由,在其中调用异步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
}

请帮助我实现这一目标

3 个答案:

答案 0 :(得分:4)

有一种更好的方法可以通过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}中的文档键。 strong>和 $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
}

答案 1 :(得分:2)

尽管@chridam方法非常独特,而且可能更有效,以防您想陷入循环。

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

如果 parallel ,则必须使用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
}

答案 2 :(得分:0)

问题是您使用async-await的方式。它位于Array.prototype.map内,该函数使.map函数异步,因此主线程从不等待循环完成并前进到下一条语句,并返回data,它就是{}。 / p>

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