I have a problem using Node JS when making synchronous calls.. Here's my problem:
I have the following code:
async.doWhilst(function(callback) {
//some code
callback();
}, function() {
//make a database call and based on the results I should
//return true to continue looping or false to stop here
}, function(err) {
//do some things when the loop finishes
})
The problem is when calling the database it is asynchronous call and the loop continues before even returning the proper value.
Thank you alot for your comments, I have solved the problem by move the database call to the loop code like this:
var results = []
async.doWhilst(function(callback) {
//some code
sequelize.query('some query').success(function(result) {
results = result;
callback();
});
}, function() {
//use the results variable that is fetched from the database
//return true to continue looping or false to stop here
}, function(err) {
//do some things when the loop finishes
})
You can have Sequelize return once the data has actually returned using this method:
return sequelize
.query("Database query here")
.success(function(result) {
//Do something with result here
else {
//Return error
}
});
Hope the following can help:
(function iterate() {
executeDatabaseCall(..., function(e,r) {
if (...conditions to continue looping are verified...)
iterate();
else
...manage the end of loop...
});
})();
If you really want to use sync call, you can use this library :
https://github.com/luciotato/waitfor
It'll permit you to make sync call using :
var syncDataReceived = wait.forMethod(collection,'insert',{data:toinsert});
I have solved the problem by move the database call to the loop code like this:
var results = []
async.doWhilst(function(callback) {
//some code
sequelize.query('some query').success(function(result) {
results = result;
callback();
});
}, function() {
//use the results variable that is fetched from the database
//return true to continue looping or false to stop here
}, function(err) {
//do some things when the loop finishes
})
Related
const collect = [];
req.body.product.forEach(function(entry) {
mongoClient.connect(databaseServerUrl, function(err, db) {
let testCollection = db.collection('Tests');
testCollection.find({Product: entry}).toArray((err, docs) => {
let waiting = docs.length;
docs.forEach(function (doc) {
collect.push(doc);
finish();
});
function finish() {
waiting--;
if (waiting === 0) {
res.send(collect);
}
}
});
db.close();
});
});
this is only getting back the first set. If I have two nodes in my array of req.body.product for example. I am only getting back the first set. But I need to get back everything not just from one Collection.
Rather than performing two queries and combining the results into one array, I suggest performing a single query that gets all of the results, which would look something like this:
mongoClient.connect(databaseServerUrl, function(err, db) {
const query = { $or: req.body.product.map(Product => ({ Product })) };
db.collection('Tests').find(query).toArray((err, docs) => {
// ...handle `err` here...
res.send(docs);
db.close();
});
});
Note that I haven't tested this since I don't have a MongoDB database in front of me.
your mongoClient.connect() is asyncronous but your loop just execute without waiting for the callback.
Try async forEach loop: enter link description here
This should solve your problem
I am pretty new to Node.js or Javascript in general when it comes to serverside stuff. Currently I am tring to validate some of the user input and set default values if something is wrong. Now if I run my validation the json object appears in the database befor my validation is completed.
The way I am doing the validation isnt maybe the best right now but if someone can explain me the behavior, I am pretty sure i can understand Javascript alot better in the future.
Is there a better way of doing validation (without mongoose or other ODM modules) with callbacks, middleware or should I use some async module?
Here is my code:
module.exports = function(app, express, todoDB, listDB, statusDB) {
var moment = require('moment');
var todoRouter = express.Router();
todoRouter.post('/', function(req, res, next) {
console.log('1');
if (!(moment(req.body.createDate).isValid())) {
req.body.createDate = moment().format("DD-MM-YYYY HH:mm:ss");
}
else {
req.body.createDate = moment(req.body.createDate).format("DD-MM-YYYY HH:mm:ss");
}
console.log('2');
if (req.body.list_id == '') {
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
if (docs == null) {
listDB.insert({list: 'Neu', index: 1});
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
console.log('AnlageListID');
console.log(docs._id);
req.body.list_id = docs._id;
});
}
else {
console.log('BestehendeListID');
console.log(docs._id);
req.body.list_id = docs._id;
}
});
}
console.log('3');
if (req.body.status_id == '') {
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
if (docs == null) {
statusDB.insert({status: 'offen', index: 1});
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
console.log('AnlageStatusID');
console.log(docs._id);
req.body.status_id = docs._id;
});
}
else {
console.log('BestehendeStatusID');
console.log(docs._id)
req.body.status_id = docs._id;
}
});
}
console.log('4');
console.log('StatusID');
console.log(req.body.status_id);
console.log('ListID');
console.log(req.body.list_id);
todoDB.insert({
todo: req.body.todo,
createDate: req.body.createDate,
endDate: req.body.endDate,
discription: req.body.discription,
comment: req.body.comment,
list_id: req.body.list_id,
priority_id: req.body.priority_id,
section_id: req.body.section_id,
user_id: req.body.user_id,
status_id: req.body.status_id,
company_id: req.body.company_id
});
res.json({message: 'TODO erfolgreich hinzugefĆ¼gt!'});
});
return todoRouter;
};
... and this is the ouput:
1
2
3
4
StatusID
ListID
POST /api/todos 200 76.136 ms - 44
BestehendeListID
M3Xh46VjVjaTFoCM
BestehendeStatusID
48v80B4fbO87c8um
PS: Its a small "project" just for me learing the MEAN Stack so I am using neDB.
If I understand correctly you try to sequentially execute a number of asynchronous calls and introduce checks in the code to validate if previous asynchronous calls have completed. This is not going to work in a general case because your checks may be processed before the asynchronous call goes through. It might work now and then just by chance, but I would not expect even that.
There are standard mechanisms for that. One of them is using promises, another one using async and yet another one if stacking up all callbacks one into another. Below I will demonstrate how to address the problem using async, but the same general idea applies to using promises. Check the async project on Github then the following part-solution will become clear:
var async = require("async")
async.waterfall([
function(next) {
listDB.findOne({list: 'Neu'}, next); // quits on error
},
function(doc, next) {
if (doc) {
return next(null, doc._id);
}
statusDB.insert({status: 'offen', index: 1}, function(err) {
if (err) return next(err); // quit on error
statusDB.findOne({status: 'offen'}, function(err, doc) {
next(err, doc._id); // quits on error
});
});
},
function(id, next) {
// do next step and so on
next();
}
],
// this is the exit function: it will get called whenever an error
// is passed to any of the `next` callbacks or when the last
// function in the waterfall series calls its `next` callback (with
// or without an error)
function(err) {
console.error("Error processing:", err)
});
I'm updating an object in an array with Mongoose. After it updates I'm firing res.send() in the callback.
If I don't fire the res.send() the update saves fine. But when I do res.send(), the entire object in the array is erased from Mongo.
landmarkSchema.findById(tileRes.worldID, function(err, lm) {
if (!lm){
console.log(err);
}
else if (req.user._id == lm.permissions.ownerID){
var min = tileRes.zooms[0];
var max = tileRes.zooms.slice(-1)[0];
if (lm.style.maps.localMapArray){
for (var i = 0; i < lm.style.maps.localMapArray.length; i++) { //better way to do this with mongo $set
if (lm.style.maps.localMapArray[i].map_marker_viewID == req.body.map_marker_viewID) {
lm.style.maps.localMapArray[i]['temp_upload_path'] = '';
lm.style.maps.localMapArray[i]['localMapID'] = tileRes.mapURL;
lm.style.maps.localMapArray[i]['localMapName'] = tileRes.worldID;
lm.markModified('style.maps.localMapArray');
lm.save(function(err, landmark) {
if (err){
console.log('error');
}
else {
console.log('updated map',landmark);
//res.status(200).send(landmark);
}
});
}
}
}
}
});
Is it a write issue where Mongo doesn't finish writing before res.send is fired?
I recommend start using async to iterate in these cases, of course you can do this without it, but you'll have to find a very good reason to avoid using it.
I don't see the rest of your method, but using async it should be similar to this:
async.each(lm.style.maps.localMapArray, function(localMap, callback) {
// Do whatever you like here
//....
// Call method that requires a callback and pass the loop callback
localMap.iUseACallbackAfterDoingMyStuff(callback);
}, function(err){
// now here the loop has ended
if( err ) {
// Something happened..
} else {
res.status(200).send(somethingToTheClient);
}
});
The code snippet is just for you to get the idea.
Ok, lets say I have two Models. Contract and CommLog. Both work find independently but I need many CommLog to relate to each Contract.
In the ContractSchema trying async
ContractSchema.methods.getCommLog = function getCommLog(){
var log = false;
async.parallel([
function(){
CommLog.find({commType:'contract',parent:this._id},function(err,comms){
log = comms;
});
}],
function(){return log;});
};
Where I am trying to use it
router.get('/:code', function(req, res, next) {
Contract.findOne({accessCode:req.params.code},function(err,contract){
if(err)
res.send(err);
var data;
if(contract != null){
var comms = contract.getCommLog();
data = {error:false,data:contract,commlog:comms}
}else{
data = {error:true,message:"No Contract"}
}
res.json(data);
});
});
Where it shows var comms = contract.getCommLog(); It is never returning anything because the getCommLog() is not executing async...
I think its my misunderstanding of mongoose querying, so if you understand what I am trying to accomplish, please let me know what I am doing wrong. I have tried without async which would always return false.
The find call can return all matching results with one query, so I don't think you need async here. The reason it is not populating correctly when you call res.json(data) is because you are not waiting for the method call to finish before you fire off your server response. You would be better off nesting an additional CommLogs.find call within the Contract.find call, and only sending your response once that finishes.
//pseudo code:
Contract.find({}, function(err, contract) {
if(err || !contract) {
//return error response
}
else {
CommLogs.find({contract: contract._id}, function(err, commlogs) {
if(err || !commlogs) {
//return error response 2
}
else {
res.json({errors: false, contract: contract, commlogs: commlogs});
}
});
}
}
I know node.js run asynchronously, so outer functions execute earlier than the inner. But what is the way to access the notification array outside the for loop? I would like to access all the values in array at once, is this feasible?
var notification=[];
for(var j=0;j<6; j++)
{
getNotification(response[j].sender_id,function(results) // a function called
{
notification[j] =results;
console.log(notification); // output: correct
});
}
console.log(notification); // output: [], need notification array values here
EDIT: If you don't want to use third party libs, this is how to do this in your own code.
/* jshint node:true*/
function getNotifications(responses, callbackToMainProgramLogic) {
'use strict';
var results = [];
function getNotificationAsync(response) {
getNotification(response.sender_id, function (data) {
results.push(data);
if (responses.length) {
getNotificationAsync(responses.pop());//If there are still responses, launch another async getNotification.
} else {
callbackToMainProgramLogic(results);//IF there aren't we're done, and we return to main program flow
}
});
}
getNotificationAsync(responses.pop());
}
getNotifications(someArrayOfResonses, function (dataFromNotifications) {
console.log('The collected data: ' + JSON.stringify(dataFromNotifications, 0, 4));
});
If you absolutely must, you could do something ridiculous like this. Your logic in the loopUntilDatReceived would be waiting for array sizes, not waiting for a non-empty string, but the idea is similar, and you shouldn't be using this anyway! :)
var fileData = '';
fs.readFile('blah.js', function (err, data) { //Async operation, similar to your issue.
'use strict';
fileData = data;
console.log('The Data: ' + data);
});
function loopUntilDataReceived() {
'use strict';
process.nextTick(function () {//A straight while loop would block the event loop, so we do this once per loop around the event loop.
if (fileData === '') {
console.log('No Data Yet');
loopUntilDataReceived();
} else {
console.log('Finally: ' + fileData);
}
});
}
loopUntilDataReceived();
Did I mention this is ridiculous? Honestly, this is an awful idea, but it may help you understand what is going on and how the Node event loop works, and why what you want is not possible. AND why the other posts about callbacks, and flow control libraries are the way to go.
First off, you're having a closure issue in your code (please see the details here)
Then, you simply can't have the array values just next to the loop, because the values are not ready at this point.
You need to wait until all 6 of your getNotification calls get resolved. You can do that with the async library. Something like:
var notification = [];
function createRequest (index) {
return function (callback) {
getNotification(response[index].sender_id, function(results) {
notification[index] = results;
callback(results);
});
}
}
var requests = [];
for(var j=0;j<6; j++) {
requests.push(createRequest(j));
}
async.parallel(requests, function (allResults) {
// notifications array is ready at this point
// the data should also be available in the allResults array
console.log(notifications);
});
Send a callback to the notification loop like this:
var notification=[];
getNotificationArray( function() {
console.log(notification);
});
function getNotificationArray (callback)
{
for(var j=0;j<6; j++)
{
getNotification(response[j].sender_id,function(results) // a function called
{
notification[j] =results;
console.log(notification); // output: correct
});
}
callback();
}