How to ensure sequential execution of nodejs in order to return a result from a query?

Refresh

April 2019

Views

40 time

1

I'm very new to nodejs and javascript in general. I'm trying to implement a response from a controller method which will return a result from a query. Currently, the response is sent before the query is finished. Would really appreciate some advice on how to approach this.

I've tried implementing a callback, but cannot seem to get it to work as expected. Setting a breakpoint on project.push() I know it pushes, only after the response has been sent.

function getProjects(callback){
    var projects = [];

    Project.find({}, function (err, docs){
      if (!err){
        docs.forEach((doc) => {

          Task.find({project_id: doc._id}, function(err, tasks){

            var tasks_count = tasks.length;
            var project = {
              name: doc.name,
              tasks: tasks_count,
              _id:  doc._id
            };

            projects.push(project);
          });
        });
      } else {
          throw err;
      }
      callback(projects);
    });
};

router.get('/projectlist', function (req, res) {
    getProjects(function(projects){
        res.json(projects);
    });
});

The code above will send an empty response and then go ahead and push the actual results to the array. Any help is greatly appreciated!

2 answers

2

To ensure sequential execution you can use the "async + await" syntax.

https://javascript.info/async-await

2

You're calling the callback in the wrong place. Notice that Task.find is asynchronous. You can use async https://www.npmjs.com/package/async to handle async calls.

function getProjects(callback) {
var projects = [];

Project.find({}, function (err, docs) { //first async call 
    if (!err) {

        async.each(docs, (doc, key, callback) => {
            Task.find({ project_id: doc._id }, function (err, tasks) { //second async call

                var tasks_count = tasks.length;
                var project = {
                    name: doc.name,
                    tasks: tasks_count,
                    _id: doc._id
                };

                projects.push(project);
                return callback(); //this callback tell async that it can move to the next element in the array
            });

        }, function (err) { //this function will be call after all the element in the array are returned 
            if (err) {
                return callback(err);
            }
            // now you can call your callback, the convention is to call the callback with callback(error, data);
            callback(null, projects); //we have no errors so we pass null
        });
    } else {
        return callback(err);
    }

});

};

I didn't run the code, but it should work. hoping the comments will help you.