Hello I'm using sequelize and I would like to make a synchronous query, but this is made asynchronously and return an wrong response.
I have this code:
function(user) {
var allow = false;
return User.find({ where: { id: user.id},
include: [{
model: Plan,
attributes: ['id', 'name']
}]
}).then(function(u) {
if(!u) throw new Error("0 user not found");
user = u.dataValues;
var plan = user.plan.dataValues.name;
else if (plan == "Premium") allow = true;
console.log("allow", allow);
return allow;
}).catch(function(error){
console.log("error:::", error);
});
}
And the console.log('allow', allow); is printing true but when I call the function, the function is returning false.
Thanks for help.
You can't make asynchronous code synchronous, so you're going to have to deal with the asynchronicity.
However, it's not too difficult, since your function is returning a promise already. You can use that to pass the allow variable to a caller:
yourFunction(user).then(function(allow) {
console.log('allow', allow);
});
I think the return allow; statement is the return value to function function(u) {} inside .then(), not the function (user) {}.
Furthermore, User.find() returns a promise. If you read about Promise, you will see there are some ways to deal with your problem.
Here is one:
var myFunc = function(user, callback) {
var allow = false;
User.find({ where: { id: user.id},
include: [{
model: Plan,
attributes: ['id', 'name']
}]
}).then(function(u) {
if(!u) throw new Error("0 user not found");
user = u.dataValues;
var plan = user.plan.dataValues.name;
else if (plan == "Premium") allow = true;
callback(null, allow);
}).catch(function(error){
callback(error);
});
}
Then you can use this function:
myFunc(user, function (err, allow) {
// Do not forget to handle error
// `allow` now could be used here
console.log(allow);
});
Related
i am pushing errors in array by using promises but before it push errors in array it check the condition for insert data like in else i am inserting data.
var errorsArr= [];
var username = new Promise(function(resolve,reject){
User.findOne({ "username": req.body.username },function(err,user){
if(err)
reject(err);
if(user) {
resolve(1);
}else{
resolve(0);
}
});
});
username.then(function(data){
if( data == 1 )
errorsArr.push({"msg": "Username already been taken."});
}).catch(function(err){
console.log(err);
});
var email = new Promise(function(resolve,reject){
User.findOne({ "email": req.body.email },function(err,user){
if(err)
reject(err);
if(user) {
resolve(1);
}else{
resolve(0);
}
});
});
email.then(function(data){
if( data == 1 )
errorsArr.push({"msg": "email already been taken."});
}).catch(function(err){
console.log(err);
});
if(errorsArr.length >0)
{
req.session.error = errorsArr;
return res.redirect('/auth/Registration');
}
else {
var newUser = new User();
newUser.username = req.body.username;
newUser.password = req.body.password;
newUser.sex = req.body.sex;
newUser.email = req.body.email;
newUser.phoneNumber = req.body.phoneNumber;
newUser.age = req.body.age;
newUser.designation = req.body.designation;
newUser.timing = req.body.timing;
var CurrentDate = moment.tz(new Date(req.body.joiningDate), "Asia/Karachi").unix();
newUser.joiningDate = CurrentDate;
newUser.save(function (err, user) {
if (!err)
return res.redirect('/auth/Login');
});
}
can you please help me to do it in better way i am new in node.js. Thanks in advance.
Promise.all is your solution. It waits for all the promises.
Promise.all([p1,p2,..]).then(function() {
// all loaded
}, function() {
// one or more failed
});
Reference Link:
http://www.html5rocks.com/en/tutorials/es6/promises/
You're mixing asynchronous and synchronous code.
Synchronous:
var errorsArr= [];
Asynchronous:
var username = new Promise(...{
...
});
username.then(..
errorsArr.push(...);
})
Synchronous:
if (errorsArr.length > 0) {
...
The asynchronous part in the middle (where you do errorsArr.push(…)) doesn't actually happen before your synchronous code at the end (if (errorsArr.length > 0)).
You need to convert all your synchronous code to asynchronous as well, if you want it to execute in succession to the other async code.
Other answers have already explained that how, which is by using Promise.all
I would also like to suggest using Bluebird Promise library which offers promisification so you can just promisify your mongoose models, instead of manually creating promises for every operation.
Promise.promisifyAll(User);
var username = User.findOneAsync(...); // the new "findOneAsync" method returns a promise
username.then(data => ...)
You might also wanna checkout the upcoming ES2016 additions to javascript that further simplify promises with async/await. With it you can get rid of .then(..) entirely and just write:
app.get('/', async function(req, res) { // < async function
try {
var username = await User.findOneAsync(...); // await statements
var email = await User.findOneAsync(...);
// do stuff
} catch(err){
// handle errors
}
});
Checkout this great article on async/await. Although this requires you add an extra transpilation step by using Babel or Typescript. There's also the recently released Microsoft's fork of Node that ships with chakra engine (instead of V8) which already supports async/await as well.
Here is an example
Promise.all([
Reservation.find({'parking.owner': req.payload.id}).exec(),
Reservation.count({'parking.owner': req.payload.id}).exec(),
Reservation.aggregate([
{ $match: {
_id: req.payload.id
}},
{ $group: {
_id: "$_id",
total: { $sum: "$price" }
}}
])
]).then(results=>{
const list = results[0]
const count = results[1]
const sum = results[2]
res.status(200).json({list,count,sum});
})
after all Promises have been done ... use Promise.all
the checking for errors at the bottom of your code could be written:
Promise.all([username, email])
.then(function(results) {
if(errorsArr.length >0)
{
...
}
else {
...
}
});
Quickest and easiest change to your code - refactoring your code can make it even sleeker
#Jaromanda X answer is correct if there is username and email checks are independent and no order has to be followed. Else, you have to do a promise chain.
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 created a function to:
take an array of 'labels' and look for whether they have a record in the db already
create those which don't exist,
and update those which do exist
return a json array reporting on each item, whether they were updated/created, or resulted in an error
I managed to make it work but I feel like I just made some ugly dogs' dinner!
var models = require("../models");
var Promise = models.Sequelize.Promise;
module.exports = {
addBeans: function (req, callback) {
Promise.map(req.body.beansArr, function (bean) {
return models.Portfolio.findOrCreate({where: {label: bean}}, {label: bean});
}).then(function (results) { // Array of 'instance' and 'created' for each bean "findOrCreate(where, [defaults], [options]) -> Promise<Instance>"
var promisesArr = [];
results.forEach(function (result) {
if (result[1]) { // result[1] = wasCreated
promisesArr.push(Promise.resolve([result[0].dataValues.label, "created"]));
} else {
promisesArr.push(
models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).then(function () {
return Promise.resolve([result[0].dataValues.label, "updated"])
})
);
}
});
return promisesArr;
// When it's all done create a JSON response
}).then(function (results) {
var resultObj = {items: []}; // JSON to return at the end
Promise.settle(results).then(function (promiseinstances) {
for (var i = 0; i < promiseInstances.length; i++) {
if (promiseInstances[i].isFulfilled()) {
resultObj.items.push({
item: {
label: promiseInstances[i].value()[0],
result: promiseInstances[i].value()[1],
error: ''
}
});
}
else if (promiseInstances[i].isRejected()){
resultObj.items.push({
label: promiseInstances[i].value()[0],
result: 'error',
error: promiseInstances[i].reason()
});
}
}
// Send the response back to caller
}).then(function () {
return callback(null, resultObj);
}, function (e) {
return callback(e, resultObj);
});
});
}
};
Question:
Is there an easier or more obvious way to create/update values with Sequelize?
Is my use of Promise.settle() appropriate for this case? I have the feeling I made this more complicated than it needs to be.
I am new to Sequelize and using Promises, I'd appreciate if someone could advise on this.
I feel like this would work better on CodeReview.SE but I can see a few issues.
Is there an easier or more obvious way to create/update values with Sequelize?
Well, for one thing:
.then(function(array){
var newArr = [];
array.forEach(function(elem){
newArr.push(fn(elem);
}
return newArr;
});
Is just
.map(fn)
Additionally, promises assimilate so you can return val; from a .then you don't have to return Promise.resolve(val);.
So:
).then(function (results) { // Array of 'instance' and 'created' for each bean "findOrCreate(where, [defaults], [options]) -> Promise<Instance>"
var promisesArr = [];
results.forEach(function (result) {
if (result[1]) { // result[1] = wasCreated
promisesArr.push(Promise.resolve([result[0].dataValues.label, "created"]));
} else {
promisesArr.push(
models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).then(function () {
return Promise.resolve([result[0].dataValues.label, "updated"])
})
);
}
});
return promisesArr;
})
Is just
.map(function(result){
if(result[1]) return [result[0].dataValues.label, "created"];
return models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).
return([result[0].dataValues.label, "updated"]);
});
However, since you want it to work regardless of it being resolved, you'd have to do:
.then(function(results){
return results.map(function(result){
if(result[1]) return [result[0].dataValues.label, "created"];
return models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).
return([result[0].dataValues.label, "updated"]);
});
});
Which means it'll resolve regardless, then you'd call .settle():
.settle().then(function(results){
// your settle logic here
});
Note that the last:
}).then(function () {
return callback(null, resultObj);
}, function (e) {
return callback(e, resultObj);
});
Is simply:
.nodeify(callback);
However, I recommend sticking to promises.
I use Promise.settle for sequelize.update, and can get affect rows number by _settledValueField .
promise.push(...update...)
db.sequelize.Promise.settle(promise).then(function (allresult) {
var affectcnt = 0
allresult.forEach(function (singlecnt) {
if (undefined !== singlecnt._settledValueField[1]) {
affectcnt += parseInt(singlecnt._settledValueField[1])
}
})
unfortunately, it's only work for update.
You can insert array in database using sequelize. You want to change in model like below. I am trying to add multiple languages in database through array.
language: { type: DataTypes.STRING,
allowNull: false,
get()
{
return this.getDataValue('language').split(';')
},
set(val)
{
this.setDataValue('language',Array.isArray(val) ? val.join(','):val);
}
}
I'm having some problems returning results from a module function.
Below are two files that I'm working with.
When I call the exported function it returns nothings.
Any suggestions/fixes as to why? Does it have to do with callbacks?
models/index.js
module.exports = exports = function(library) {
modCodes.findOne({name: library}, {modcode:1}, function(err, mc) {
if (err) throw new Error(err);
var db = mongoose.createConnection('mongodb://localhost:27017/' + mc.modcode + '?safe=true');
var models = {
Books: db.model('books', require('./schemas/books'))
}
return models;
});
};
books.js
var Models = require('../models');
console.log(Models("myLibrary")); //return nothing
The reason you're getting no results is that you're trying to return a function value synchronously from an asynchronous callback. Instead of providing a function value, the return statement will instead stop the function, as return; would normally do. This is why you must use a callback for asynchronous operations:
module.exports = exports = function(library, callback) {
modCodes.findOne({name: library}, {modcode: 1}, function (err, mc) {
if (err) throw new Error(err);
var db = mongoose.createConnection('mongodb://localhost:27017/' + mc.modcode + '?safe=true');
var models = {
Books: db.model('books', require('./schemas/books'))
}
callback(models);
});
};
And this is how you would be able to use it:
var Models = require('../models');
Models('myLibrary', function(models) {
console.log(models);
});
i solve similar problem in different way. I am not sure whether it is right way.
in main node js I am using a model named product. I am passing product and res to misc.js. Following is part of my server.js file
var misc = require('./misc');
app.get('/groupbyCategory', function(req,res,next)
{
var res2;
misc.addX(product,res);
})
IN misc.js doing group by function and will return that value to straight way to angular controller. it is not necessary to return the result to server.js and from server.js to return angular controller. So i feel waiting and other call back seems unnecessary.
Inside misc.js i keep following code.
exports.addX = function(product,res) {
product.aggregate([
{ $group: {
_id: {category: "$category"},
count: { $sum: 1 }
}}
], function (err, result) {
if (err) {
console.log(err);
return err;
}
else
{
//return result;
console.log(result);
res.send(result);
}
});
};
It should be very awesome to use non-blocking code but I'm running out of ideas how to acomplish this task. I have to validate a value by making few db queries like so:
validate = function() {
var valid = true;
db.collection('posts').findOne({date: ....}, function(err, post){
if (..) valid = false
}
db.collection('posts').findOne({author: .....}, function(err, post){
if (..) valid = false
}
return valid;
}
It is very good that validations can run in concurent manner, but the problem is how to return the final state. Obviously my example will not work. The function will return before db queries execution.
Welcome to the async world.
You should use something like async or fnqueue for your control flow,
then you can setup a chain of validations.
function isValid (mainCallback) {
new FnQueue({
date: function (callback) {
if (...) {
callback();
} else {
callback(new Error('what\'s happened here');
}
},
author: function (callback) {
db.collection('posts').findOne({ author: ..... }, callback);
}
},
function (err, data) {
mainCallback(Boolean(err)); //you should more than this :)
},
1 // concurrency level for serial execution
);
If you are using mongoose, then you can use the validations that are supported in the models. Take a look the validation docs for details and examples.
If you are not using mongoose, then you will need to pass a callback to your validate function, and the callback will receive the boolean. Also, you will need to handle the flow of your function so that they are run in series or parallel, depending on your needs. So if it is in in series, the following would work:
validate = function(callback) {
var valid = true;
db.collection('posts').findOne({date: ....}, function(err, post){
if (..) {
return callback(true);
}
db.collection('posts').findOne({author: .....}, function(err, post){
if (..) callback(false);
});
});
}