问题
Here is simple node route in which calling an asynchronous api
.
What needed is to return data
after the looping. But It is returning blank object.
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
}
Please help me to achieve this
回答1:
There is a better way to get the desired result with MongoDB and no need to loop, use the aggregation framework where you can run the following pipeline which uses $facet as
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
}
You can also group the documents by the category key and $push the documents per group and then convert into keys of a document in a $replaceRoot with $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
}
回答2:
Although @chridam approach is quite unique and maybe more efficient, in case you want to stick with a loop.
There are two approaches. You want all your operation to be run in parallel or series.
If parallel, you will have to use 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
}
If series, you will have to use simple for
loop.
array.map(async(d) => {})
is making the internal db call asynchronous and not waiting for operation. normal for
loop will be synchronous.
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
}
回答3:
Problem is the way you are using async-await
. It's inside a Array.prototype.map
which made .map
function asynchronous and thus main thread never waits for loop to complete and move forward to next statement and returned data
which is just {}
.
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
}
Change code to this:
(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
}
})();
What you did is something similar to this
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
来源:https://stackoverflow.com/questions/52534679/why-even-after-using-async-await-for-multiple-calls-still-got-empty-response