John John - 2 months ago 7
Javascript Question

Node.js detect when two mongoose find are finished

I'm trying to initialize two input with autocomplete with this library. When I load my page, I will trigger an Ajax to initialize two input text.

But I don't know how I can detect when all my mongoose find are completed.

Here is my server side code :

app.post('/init/autocomplete', function(req, res){
var autocomplete = {
companies: [],
offices: []
};

// Find all companies
Company.find({}, function(err, companies) {
if (err) throw err;

companies.forEach(function(company) {
autocomplete.companies.push({value: company.name})
});

console.log('One');
});

// Find all offices
Office.find({}, function(err, offices) {
if (err) throw err;

offices.forEach(function(office) {
autocomplete.offices.push({value: office.name})
});

console.log('Two');
});

console.log('Three');

// res.json(autocomplete);
});


I know than the find method is async. That is why I see my console.log() in this order :

Three
One
Two


How can I do to trigger
console.log('Three');
when the
Company.find
and
Office.find
are finished ?

I want to see the
console.log('Three');
at the last position.

Edit :

I think I can do this way :

app.post('/init/autocomplete', function(req, res){
var autocomplete = {
companies: [],
offices: []
};

// Find all companies
Company.find({}, function(err, companies) {
if (err) throw err;

companies.forEach(function(company) {
autocomplete.companies.push({value: company.name})
});

// Find all offices
Office.find({}, function(err, offices) {
if (err) throw err;

offices.forEach(function(office) {
autocomplete.offices.push({value: office.name})
});

res.json(autocomplete);
});
});
});


But I don't know if it's the good way. Maybe using promise will be better ? I'm open for all suggestions.

Answer

use Promise. There are ways to control parallel and series. and your code tends to be much readable. My method of dealing with parallel is to execute the async part first, then when the result have been collected, do the synchronous part.

app.post('/init/autocomplete', function(req, res){
    // Find all companies
    // the query action below is not executed, just return PromiseObject for now
    var findCompanies = new Promise((resolve, reject) => {
      Company.find({}, function(err, companies) {
          if (err) reject(err);
          resolve(companies)                   
       });
    })

    // Find all offices
    // the query action below is not executed, just return PromiseObject for now
    var findOffices = new Promise((resolve, reject) => {
      Office.find({}, function(err, offices) {
          if (err) reject(err);

          resolve(offices)
      });
    })

    // this is the execution part, in OOP world, you can think this is main()
    // execute and wait until each action(Promise Object) is complete before finally returning an array.
    return Promise.all([findCompanies, findOffices])
                  .then(array => {
                       console.log(array) // [ [companies] , [offices]  ]
                       //the difficult async part is done, with just few lines

                       console.log(array[0]) // [companies] array of companies
                       console.log(array[1]) // [offices] array of offices

                       // now you can safely manipulate using forEach.
                       // basically, this part is for the synchronous action               

                        var autocomplete = {};

                        array[0].forEach(function(company) {
                            autocomplete.companies.push({value: company.name})
                        });
                        array[1].forEach(function(office) {
                            autocomplete.office.push({value: office.name})
                        });
                       res.json(autocomplete)
                  })
});

the code above, findCompanies and FindOffices are Promise Object store in variable(it is not yet executed). Next, with Promise.all(), we run all the Promise Objects, and it will wait until each action is complete before finally returning an array.

the returned array contains another two array. The sequence of this array is the same as the sequence of actions you pass to Promise.all()

if you findOffices first, then findCompanies. It will return [[offices],[companies]] Promise.all([findOffices, findCompanies]) will return [[offices], [companies]]

vice versa

Promise.all([findCompanies, findOffices]) will return [[companies], [offices]]