简体   繁体   中英

javascript how use async await inside of a cycle in parallel

I have the next example in express js and mongoose. Exist a relationship between student and province:

app.get('/example', async (req, res) => {
const provinces = await Province.find();
let studentsByProvince = [];
for (let prov of provinces) {
    const st = await Student.count({ province: prov });
    studentsByProvince.push({ province: prov.province, totalStudents: st });
}
res.json(studentsByProvince);});

This is not efficient since inside the cycle the search is carried out sequentially. I solve it this way:

app.get('/example2', async (req, res) => {
const provinces = await Province.find();
let studentsByProvince = [];
let studentsByProvincePromises = [];
for (let prov of provinces) {
    const studentPromise = Student.count({ province: prov });
    studentsByProvincePromises.push(studentPromise);
}
const studentsByProvinceResult = await Promise.all(studentsByProvincePromises);

for (let [index, prov] of provinces.entries()) {
    studentsByProvince.push({ province: prov.province, totalStudents: studentsByProvinceResult[index] });
}
res.json(studentsByProvince);});

I already resolved the issue of executing it in parallel, however I have to go through the cycle twice, since the query returns a promise and not the result. There is async await some way to solve this example similar to the first way, but in parallel.

You can attach a callback to the promises in the first loop:

app.get('/example2', async (req, res) => {
    const provinces = await Province.find();
    let studentsByProvincePromises = [];
    for (let prov of provinces) {
        const studentPromise = Student.count({ province: prov })
            .then(st => ({ province: prov.province, totalStudents: st });
        studentsByProvincePromises.push(studentPromise);
    }
    const studentsByProvince = await Promise.all(studentsByProvincePromises);

    res.json(studentsByProvince);
});

Using Array.prototype.map will make stuff look more concise:

app.get('/example2', async (req, res) => {
    const provinces = await Province.find();

    const studentsByProvince = await Promise.all(
        provinces.map(prov => {
            return (Student.count({ province: prov })
                .then(totalStudents => ({ province: prov.province, totalStudents })
            )
        })
    );

    res.json(studentsByProvince);
});

I think you can achieve this differently and even save a database query. Since you want all provinces anyways (you can also programmatically add a filter to the pipeline) why not do an aggregation?

Student.aggregate([
  {
    $group:{
      _id: "$province", // this is the property we are grouping by 
      count: { $sum: 1 }
    }   
  }
])

This will return an array of objects with the following structure:

[
  {
    "_id" : "provinceName",
    "count" : 6
  },
  ....
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM