Node.js 检测两个猫鼬查找何时完成
Posted
技术标签:
【中文标题】Node.js 检测两个猫鼬查找何时完成【英文标题】:Node.js detect when two mongoose find are finished 【发布时间】:2017-01-21 01:24:27 【问题描述】:我正在尝试使用 library 使用自动完成功能初始化两个输入。当我加载我的页面时,我会触发一个 Ajax 来初始化两个输入文本。
但我不知道如何检测我的所有 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);
);
我知道 find 方法是异步的。这就是为什么我按这个顺序看到我的 console.log() :
Three
One
Two
当Company.find
和Office.find
完成时,我该如何触发console.log('Three');
?
我想在最后一个位置看到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);
);
);
);
但我不知道这是否是好方法。也许使用 promise 会更好?我愿意接受所有建议。
【问题讨论】:
使用 Promise,用 Promise 可以很轻松的解决并列问题 【参考方案1】:Mongoose 内置了对 Promise 的支持,这些 Promise 提供了一种干净的方式来等待使用 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.
);
);
【讨论】:
很好,但是猫鼬如何处理它的内部错误?出现错误时它会中断吗?还是将所有内容返回结果?实际上,在某些情况下,我更愿意收集所有结果,并让客户端处理哪个查询成功或失败 @vdj4y 如果查询失败,则拒绝承诺并调用catch
处理程序。在需要以不同方式处理部分成功的情况下,您不会使用Promise.all
。您基本上是在用Promise.all
说“全有或全无”。
我明白了,有时候,它会很好。我认为如果手动创建 Promise,我可以选择 resolve(err) 并立即返回。但这应该是我在其他情况下的首选代码。谢谢
感谢您的回答。这对我的情况来说是个好主意
.map() 比 forEach 有更好的性能?我从来没有见过它,它看起来很不错【参考方案2】:
使用Promise。有一些方法可以控制并联和串联。而且您的代码往往更具可读性。我处理并行的方法是先执行异步部分,然后在收集到结果后执行同步部分。
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 对象存储在变量中(它还没有执行)。接下来,使用 Promise.all(),我们运行所有的 Promise 对象,它会等到每个动作完成后才最终返回一个数组。
返回的数组包含另外两个数组。这个数组的顺序和你传递给 Promise.all() 的动作顺序是一样的
如果您先找到办公室,然后再找到公司。它将返回 [[offices],[companies]]
Promise.all([findOffices, findCompanies])
将返回 [[offices], [companies]]
反之亦然
Promise.all([findCompanies, findOffices])
将返回 [[companies], [offices]]
【讨论】:
非常好的答案,谢谢。所以我可以用 array[0].name 在company.name
上做我的 foreach 吗?这个数组将包含我的 mongodb json 对象?并且最好使用 Native Promise 或 bluebird ?
array[0] is [companies] ,你可以做 array[0].forEach( (company) => //push to autocomplete )
数组的序列是你传递给 Promise.all() 的序列,如果你传递 [offices, Companies] 它将返回 [[offices], [companies] ]。反之亦然
我必须删除 return 关键字,因为我有这个错误:var findCompanies = return new Promise(function(resolve, reject)
SyntaxError: Unexpected token return
。为了创建 forEach 循环,我必须使用 companies
和 offices
数组初始化我的自动完成变量,否则循环将停止。
哦,是的,对不起,我忘了它是一个变量而不是一个函数。将更新代码【参考方案3】:
使用 Promises,您将拥有更清晰的代码来维护而不会产生太多开销,使用 Mongoose 您有两个选择。使用本机 Mongoose 承诺,这对于基本用例来说很好。但是 Mongoose 的承诺目前已被弃用,因为会出现警告。
所以要切换到原生 Promises:
// Native JS Promise
mongoose.Promise = global.Promise;
或者到更高级的Bluebird Promise 库:
// Bluebird
mongoose.Promise = require('bluebird');
带有原生 JS 承诺的 MVCE 和 Bluebird 已被注释掉。
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 Promises 的文档。
【讨论】:
Promise.all()
在这种情况下也很有用。
感谢您的回答。我从不在节点 js 中使用 promise,这将是我的第一次。 @vdj4y 的答案和您的答案有两种方法。有什么区别?
基本上Promise.all 在所有承诺都解决后返回一个承诺。如果任何传入的 Promise 被拒绝,所有 Promise 立即以被拒绝的 Promise 的值拒绝,丢弃所有其他 Promise,无论它们是否已解决。所以如果你想要这种行为是要走的路。在我的情况下,您可以处理流程中的拒绝。
另外我使用的是同步方法而不是@vdj4y,它使用了一种异步方法,这对性能有好处,但你会失去对执行流程的控制。一个很好的解释:***.com/questions/28066429/…
在我的情况下,我更喜欢异步。我需要的不是我所有的承诺都是决心。但我记住了你的回答,因为我认为这对我将来真的有帮助。谢谢【参考方案4】:
可以使用你指定的方法,但是对于大量的异步操作,它会导致callback from hell。为了避免这种情况,最好以顺序和有序的方式编写代码。在您的情况下,您可以使用 async 之类的库来实现此目的,因为 mongoose 不会返回 Promise Object
。有关异步库的更多信息,您可以参考此链接。
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');
);
只有在任务一和二完成后才会写入第三个日志。 series
函数一一运行任务。您可以使用eachSeries 并行运行所有任务。
【讨论】:
【参考方案5】:我对 mongoose 很陌生,但我遇到了类似的问题,我通过使用 cursor() 解决了它
let cursor = Company.find().cursor();
cursor.on('data', function(doc)
//do something with doc
)
cursor.on('close', function()
console.log("done");
)
【讨论】:
以上是关于Node.js 检测两个猫鼬查找何时完成的主要内容,如果未能解决你的问题,请参考以下文章