Node.js检测两个mongoose发现完成时

我正尝试用这个库自动完成初始化两个input。 当我加载我的页面时,我将触发一个Ajax来初始化两个input文本。

但是我不知道当我所有的mongoose发现什么时候完成的时候我怎么能发现。

这是我的服务器端代码:

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); }); 

我知道比查找方法是asynchronous的。 这就是为什么我按照以下顺序看到我的console.log():

 Three One Two 

我怎样才能触发console.log('Three');Company.findOffice.find完成时?

我想看到console.log('Three'); 在最后的位置。

编辑:

我想我可以这样做:

 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); }); }); }); 

但我不知道这是不是好方法。 也许用诺言会更好? 我接受所有build议。

Mongoose内置了Promise.all支持,它提供了一个干净的方式来等待Promise.all的多个asynchronous查询操作的完成:

 // 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. }); }); 

使用Promise 。 有办法来控制并行和系列。 而且你的代码往往可读性强。 我的处理并行的方法是首先执行asynchronous部分,然后在收集结果时执行同步部分。

 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) }) }); 

上面的代码中,FindCompanies和FindOffices都是Promise Object store中的variables(尚未执行)。 接下来,使用Promise.all(),我们运行所有的Promise对象,并在最后返回一个数组之前,等待每个动作完成。

返回的数组包含另外两个数组。 该数组的顺序与传递给Promise.all()的操作顺序相同

如果你先find办公室,然后find公司。 它将返回[[办公室],[公司]] Promise.all([findOffices, findCompanies])将返回[[offices], [companies]]

反之亦然

Promise.all([findCompanies, findOffices])将返回[[companies], [offices]]

有了承诺,你将有一个更清晰的代码来维护,没有太多的开销,使用Mongoose你有两个select。 使用本地Mongoose的承诺这是很好的基本用例。 但是mongoose的承诺在这个时候会被抛弃,因为会出现警告。

所以要切换到原生Promise:

 // Native JS Promise mongoose.Promise = global.Promise; 

或者到更先进的蓝鸟诺言图书馆:

 // Bluebird mongoose.Promise = require('bluebird'); 

原生JS承诺MVH和蓝鸟评论。

 const mongoose = require('mongoose'); const Schema = mongoose.Schema; const assert = require('assert'); mongoose.connect("mongodb://localhost:33023/test_1"); // Native JS Promise mongoose.Promise = global.Promise; // Bluebird // mongoose.Promise = require('bluebird'); // Schema creation var Company = mongoose.model('Company', new Schema({ name: String })); var Office = mongoose.model('Office', new Schema({ name: String })); var acme = new Company({ name: 'ACME' }); var HR = new Office({ name: 'Human Resources' }); acme .save() .then(HR.save()); var query = Company.find({}); assert.equal(query.exec().constructor, global.Promise); // assert.equal(query.exec().constructor, require('bluebird')); query.exec() .then(function(results) { console.log(results); }) .then(Office.find({}) .then(function(results) { console.log(results); })); 

Mongoose承诺的文档。

你指定的方法可以被使用,但是对于大量的asynchronous操作,会导致从地狱callback 。 为了避免这种情况,以顺序和有序的方式编写代码总是最好的。 在你的情况下,你可以使用像async这样的库来实现这一点,因为mongoose不会返回一个Promise Object 。 有关asynchronous库的更多信息,请参阅此链接。

 async.series([ function(callback) { // do some stuff ... console.log('one'); callback(null, 'one'); }, function(callback) { // do some more stuff ... console.log('two'); callback(null, 'two'); } ], // optional callback function(err, results) { // results is now equal to ['one', 'two'] console.log('three'); }); 

第三个日志只有在完成第一个任务和第二个任务后才会写入。 seriesfunction逐个运行任务。 您可以使用eachSeries并行地运行所有任务。

我对mongoose很新,但是我遇到了一个类似的问题,我使用游标()来解决它。

 let cursor = Company.find().cursor(); cursor.on('data', function(doc){ //do something with doc }) cursor.on('close', function() { console.log("done"); })