Mongoose async call inside a loop with async library Mongoose async call inside a loop with async library mongoose mongoose

Mongoose async call inside a loop with async library


You have 2 tasks: get the roots and then get the children using the roots.

If I were to do this using async.js, I would use a combination of async.waterfall and async.mapSeries. We use .waterfall because we want to pass the results from the first task to the second task. We use .mapSeries because we want to alter each root area with its name and children.

areaSchema.statics.getAreasRoot = function (cb) {    let self = this;    async.waterfall([        // every task has a callback that must be fired at least/most once        // to tell that the task has finished OR when an error has occurred        function getRoots (cb1) {            self.find({ parentId: null }, cb1);        },        function getChildren (roots, cb2) {            async.mapSeries(roots, function (root, cb3) {                // inside this block, we want to fire the innest callback cb3 when                 // each iteration is done OR when an error occurs to stop .mapSeries                self.find({ parentId: root._id }, function (err, children) {                    // error: stop .mapSeries                    if (err)                        return cb3(err);                    root.name = "Hi " + root.name;                    root.children = children;                    // done: send back the altered document                    cb3(null, root);                });            // the last argument is fired when .mapSeries has finished its iterations            // OR when an error has occurred; we simply pass the inner callback cb2            }, cb2)         }    // the last argument is fired when .waterfall has finished its tasks    // OR when an error has occurred; we simply pass the original callback cb    ], cb);};

To use it

Area.getAreasRoot(function (err, areas) {    console.log(err, areas);})

Aside

Mongoose operations are asynchronous, so

doc.children = self.model("Area").getAreasChildren(...) 

is not correct as you are returning a Promise opposed to the actual documents.

Also

You might be able to simplify your logic with virtual population or aggregation.