У меня есть две коллекции мангустов. Первый хранит список мест, второй - посещение мест. Пройдет мой node код и попытается получить список посещений в каждом месте и построить строку, которую я выводил как JSON. Первый запрос завершается до начала второго запуска - есть ли способ заставить их работать синхронно?
Выполнение запросов mongoose.js синхронно
Ответ 1
Если вы используете 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
}
Ответ 2
Существует не родной синхронный api для запросов mongodb/mongoose (и вы не захотите этого в практичности). Как упоминает 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)
}
});
}
});
}
Ответ 3
Если вы используете Node 8.x, вы можете использовать async/wait: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
Операторwait приостанавливает выполнение async-функции до тех пор, пока 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();
Запросы будут выполняться последовательно.
Ответ 4
Для синхронизации я использовал es6-обещание.
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"
}
]
}
]
Ответ 5
В наши дни mongoose поддерживает promises, поэтому вы можете .then()
ваши запросы. Например:
app.get(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 => {
//console.error(error);
//res.json({success: false, error: error.message});
next(error);
});
});
Ответ 6
Вот альтернативный метод для создания псевдосинхронных запросов с использованием 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;