我有两个猫鼬系列。第一个存储地点列表,第二个是访问地点。我的节点代码通过并尝试获取每个地方的访问列表并构建一个我输出为 JSON 的字符串。第一个查询在第二个查询开始之前完成 - 有没有办法让它们同步运行?
7 回答
如果您使用的是 node.js,那么您应该使用https://github.com/caolan/async
当您必须从多个集合中获取数据时,您必须多次链接查询。
它将使您的代码复杂且难以阅读并且没有模块化。使用 async 使用 mongodb 和 node.js 创建模块化
我的项目中的示例代码:
var async = require('async');
var createGlobalGroup = function(socket, data) {
async.waterfall(
[
/**
* this function is required to pass data recieved from client
* @param {Function} callback To pass data recieved from client
*/
function(callback) {
callback(null, socket, data);
},
/**
* Step 1: Verify User
*/
verifyUser,
/**
* Step 2: Check User Access Rights And Roles
*/
checkUserAccessRightsAndRoles,
/**
* Step 3: Create Project
*/
createNewGlobalGroup], function(err, result) {
/**
* function to be called when all functions in async array has been called
*/
console.log('project created ....')
});
}
verifyUser = function(socket, data, callback) {
//do your query
/**
* call next function in series
* provide sufficient input to next function
*/
callback(null, socket, data, {
"isValidUser": true,
});
}
checkUserAccessRightsAndRoles = function(socket, data, asyncObj, callback) {
//do your query
if(condition) {
callback(null, socket, data, {
roles: result,
"isValidUser": asyncObj.isValidUser,
"userId": asyncObj.userId,
});
} else {
//no call back
}
}
var createNewGlobalGroup = function(socket, data, asyncObj, callback) {
//wanna stop then no callback
}
没有用于 mongodb/mongoose 查询的本机同步 api(实际上你也不需要)。正如 WiredPrarie 所提到的,您应该链接查询,第二个在第一个完成后开始并运行回调。这是一个例子:
function findVisits(placesQuery,callback){
Places.find(placesQuery).exec(function(err,places){
if (err || !places.length){
console.log('there was a problem');
callback(err, null);
}else{
var visitQuery = ... //however you want to filter places
Visits.find(visitQuery).exec(function(err2,visits){
if (err2 || !visits.length){
console.log('there was a problem');
callback(err2,null);
}else{
callback(null, visits)
}
});
}
});
}
如果您使用的是 Node 8.x,则可以使用 async/await:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
await 运算符暂停异步函数的执行,直到 Promise 被解决并返回值。这样您的代码将看起来更加同步:
const query1 = MyModel.find({ name: /john/i }, null, { skip: 10 });
const result1 = await query1.exec();
const query2 = MyModel.find({ name: /john/i }, null, { skip: 100 });
const result2 = await query2.exec();
查询将连续执行。
旧解决方案:承诺
如今,Mongoose 支持 Promise,因此您可以.then()
查询。例如:
app.get('/notifications', function (req, res, next) {
Users.findOne({
username: req.body.username,
password: req.body.password,
}).then(user => {
if (!user) {
res.json({success: false, message: "Username or password incorrect."});
return;
}
return Notifications.find({
user: user._id
}).then(notifications => {
res.json({success: true, notifications});
});
).catch(error => {
// Standard way to handle errors in express
next(error);
// Or custom handling
//console.error(error);
//res.json({success: false, error: error.message});
});
});
新解决方案:异步等待
现在 Javascript 具有 async-await,您可以使用它,这将节省几行代码,并将代码扁平化一点:
app.get('/notifications', async (req, res, next) => {
try {
const user = await Users.findOne({
username: req.body.username,
password: req.body.password,
});
if (!user) {
res.json({success: false, message: "Username or password incorrect."});
return;
}
const notifications = await Notifications.find({
user: user._id
});
res.json({success: true, notifications});
} catch (error) {
next(error);
}
});
我的首选解决方案:清理异步等待
就个人而言,我不喜欢将async
关键字添加到 express 回调函数中,因为它实际上不应该是一个异步函数:我不打算从中返回一个 Promise。
我更喜欢使用IIAFE在同步和异步代码之间进行显式转换:
app.get('/notifications', (req, res, next) => {
(async () => {
const user = await Users.findOne({
username: req.body.username,
password: req.body.password,
});
if (!user) {
res.json({success: false, message: "Username or password incorrect."});
return;
}
const notifications = await Notifications.find({
user: user._id
});
res.json({success: true, notifications});
})().catch(error => {
next(error);
});
// Remember to use () to call the async function!
// You can also shrink the above to simply .catch(next);
});
不要忘记捕捉错误!
无论您使用哪种方法,如果 async 函数返回错误(以被拒绝的承诺的形式),并且 express 不处理该错误,这称为 an
unhandled rejection
,并且 Node 可能会决定使您的进程崩溃!
为了同步,我使用了 es6-promise。
var Promise = require('es6-promise').Promise
, mongoose = require('mongoose')
, Schema = mongoose.Schema;
// define schemas and models.
var placeSchema = new Schema({
name: { type: String },
memo: { type: String }
})
, Places = mongoose.model('place', placeSchema)
, visitSchema = new Schema({
placeName: { type: String }, // foreign key for place.
visitor: { type: String },
comment: { type: String }
})
, Visits = mongoose.model('visit', visitSchema);
// query for visits by visitor and place.
function findVisitsWithPlace(visitor, place) {
return new Promise(function (resolve, reject) {
Visits.find({
visitor: visitor,
placeName: place.name
}, function (error, visits) {
if (error) {
reject(error);
return;
}
// build a result object you want.
// ()
resolve({
place: place,
visits: visits
});
});
});
}
// functions for node route.
module.exports = {
// - access to "GET /placevisits/?visitor=Visitor-1".
get: function (request, response) {
var visitor = request.query.visitor;
// - to get the places...
Places.find({}, function (error, places) {
Promise.all(places.map(function (place) {
// - run the child queries with parent object...
return findVisitsWithPlace(visitor, place);
})).then(function (placeAndVisits) {
// - and get result.
// placeAndVisits have still contain visits empty.
// exclude them.
var result = [];
placeAndVisits.forEach(function (placeandvisit) {
if (placeandvisit.visits.length != 0) {
result.push(placeandvisit);
}
});
response.json(result);
});
});
}
};
我得到了如下的 JSON。
[
{
"place": {
"_id": "564e58a1dbed862155771d46",
"name": "Place-A",
"memo": "A memo for Place A."
},
"visits": [
{
"_id": "564e58cedbed862155771d49",
"placeName": "Place-A",
"visitor": "Visitor-1",
"comment": "A comment for Place A by Visitor-1"
},
{
"_id": "564e58dcdbed862155771d4a",
"placeName": "Place-A",
"visitor": "Visitor-1",
"comment": "2nd visit. Again comment for Place A by Visitor-1"
}
]
},
{
"place": {
"_id": "564e58afdbed862155771d47",
"name": "Place-B",
"memo": "A memo for Place B."
},
"visits": [
{
"_id": "564e58ebdbed862155771d4c",
"placeName": "Place-B",
"visitor": "Visitor-1",
"comment": "A comment for Place B by Visitor-1"
}
]
}
]
这是使用 MongooseJS 发出伪同步请求的另一种方法。这里的想法是创建一个需要执行的查询队列。然后创建一个递归调用的函数,直到队列耗尽。一旦队列用尽,递归停止并为原始请求发回响应。我正在使用 Express Routes,因此所有这些代码都封装在我的路由处理程序中。在这种情况下是一个 HTTP POST。
var express = require('express');
var router = express.Router();
//POST /auth/create
router.post('/create', function(req, res) {
var queue = [
{"schema": require('..\\models\\people.js'), "query": {username: req.body.username}},
{"schema": require('..\\models\\members.js'), "query": {username: req.body.username}}
],
retData = [];
var curTask = 0.
function recurse()
{
if(curTask < queue.length){
var task = queue[curTask];
task.schema.findOne(task.query, function(err, data){
retData.push(err || data);
curTask++;
recurse();
})
}else{
res.json(retData);
}
}
recurse();
});
module.exports = router;
这就是我今晚最终要做的事情。
mongoose.createConnection("mongodb://localhost:27017/chemresearch")
.then(async db => {
const collection = db.collection("chemical");
const all = collection.find({});
while(all.hasNext()) {
let chemical = await all.next();
await work(chemical);
}
});
该work
方法只是返回一个承诺。
const work = (chemical) => new Promise((resolve, reject) => {
const casNumber = chemical.casRegistryNumber;
analysis(casNumber, (error) => {
error ? reject(error) : resolve(casNumber);
})
});