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.
Mongoose has built-in support for promises which provide a clean way to wait for the completion of multiple async query operations with Promise.all
:
// Tell Mongoose to use the native Node.js promise library.
mongoose.Promise = global.Promise;
app.post('/init/autocomplete', function(req, res){
var autocomplete = {
companies: [],
offices: []
};
// Call .exec() on each query without a callback to return its promise.
Promise.all([Company.find({}).exec(), Office.find({}).exec()])
.then(results => {
// results is an array of the results of each promise, in order.
autocomplete.companies = results[0].map(c => ({value: c.name}));
autocomplete.offices = results[1].map(o => ({value: o.name}));
res.json(autocomplete);
})
.catch(err => {
throw err; // res.sendStatus(500) might be better here.
});
});
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]]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With