node.js deferred misunderstanding - node.js

I`m learning q.js and trying to query 3 collections simultaneously with its help (avoiding callback hell):
var Q = require('q')
var deferred = Q.defer();
users() is a wrapper of db.collection.find()
var users = function (){
Auth.listUsers({role:'user'}, call)
return deferred.promise
}
call() is a shorthand for exporting promises
var call = function (err,data){
if (err) {
deferred.reject(err);
} else {
deferred.resolve(data);
}
}
loop() is main loop, which gets cursor and loops through entries
var loop = function (result) {
var list = []
var promises = [];
result.each(function (err,data){
if (err) {
deferred.reject(err);
} else {
deferred.resolve(data);
promises.push(deferred.promise)
console.log('promises_:', promises) // <- internal
}
})
console.log('promises:', promises) // <- external
return Q.all(promises)
}
code:
users()
.then(loop)
.then(function(ful){
console.log('ful:', ful); // <- here should come the queries to other collections
})
result of console logging at the end:
promises: [] //external is empty
ful: []
promises_: [ [object Object] ] // internal is being filled
promises_: [ [object Object], [object Object] ]
As you can see, the callback of .each is executed later than pushing promises to array.
I believe it can be made by using result.toArray() instead of .each, but how can it be done with the help of .each loop?
result is a cursor, returned by mongodb driver after db.collection.find() call (http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#find).

var deferred = Q.defer();
…
deferred.resolve(…);
…
deferred.resolve(…);
call() is a shorthand for exporting promises
That will not work! You need to create a new deferred for each promise that you want. However, you shouldn't use Deferreds anyway! Instead, use the many Node callback helper functions.
As you can see, the callback of .each is executed later than pushing promises to array. I believe it can be made by using result.toArray() instead of .each, but how can it be done with the help of .each loop?
It cannot, unless you know beforehand how often each will be called and how many promises will need to be created. Q.all is a bit useless in here, since the promises are not created at once and execute their tasks in parallel - instead, they are a stream.
You really should use toArray here, to get a single callback with which you resolve the promise.
Well, there is a way, but it's ugly and less efficient than toArray. You can have one deferred that will always wait, and is only resolved with a promise for the rest of the stream.
function loop(result) {
var deferred = Q.defer();
result.each(function (err,data){
if (err) {
deferred.reject(err);
} else if (data == null) {
deferred.resolve([]); // end of the stream
} else {
var nextDeferred = Q.defer();
deferred.resolve(nextDeferred.promise.then(function(rest) {
return [data].concat(rest);
}));
deferred = nextDeferred;
}
})
return deferred.promise;
}

Related

NodeJS sync flow

Hello i have been using a library called deasync it allows me to get things done in a cleaner way, i have a method like this,
syncGetMany: function(model, query){
var ret;
setTimeout(function(){
model.find(query, (error, body) => {
if(body){
//Awesome got the data
ret= body
}
else{
//What a cruel world
// No data, fall back to an empty array
ret = [];
}
});
},0);
while(ret === undefined) {
require('deasync').sleep(0);
}
//returns an empty array or the real data
return ret;
},
Then i simply call it like this.
var data = syncGetMany(MongooseModel, queryObj);
// the rest of my code
QUESTION: is there a way to get this done using ES6, or any similar library.
PS: Not duplicate as other questions are not relevant to my context
simplest way to get this code more cleaner is to use async/await, it's available in node.js version => 7.0. If I think well your model.find method returns promise.
async syncGetMany (model, query) {
let ret;
ret = await model.find(query);
//ret is keeping body from response now, if error occured error is throwed as promise exception
//Could do some sync ops on ret variable
return ret;
}
When you are using async/await you should put your method execution into try/catch. But you can also catch errors inside syncGetMany method, probably it should looks like this:
async syncGetMany(model, query) {
let ret;
try {
ret = await model.find(query);
return ret;
} catch(err) {
console.error(err);
return []; //empty array or any error string/object
}
}
And your execution looks like you wrote with additional await operator (using es6 provide let/const operators for var operator replacement)
let data = await syncGetMany(MongooseModel, queryObj);
Article with async/await explanation:
https://blog.risingstack.com/async-await-node-js-7-nightly/
If you don't want to use Node v7.0 you should code something like this using promises and generators.
https://medium.com/#rdsubhas/es6-from-callbacks-to-promises-to-generators-87f1c0cd8f2e
I hope I helped.
Update
So when your mongoose instance doesn't supports promises there are three options (depends I think on node versions). In my opinion promises are cleaner way to make asynchronous request so I suggest using them.
First option (native promises):
syncGetMany(model, query) {
return new Promise((resolve, reject) => {
model.find(query, (err, res) => {
if(err) {
return reject(err);
}
resolve(res);
});
});
}
Second option (bluebird library, added Async postfix to all methods):
const mongoose = require( 'mongoose' );
const Promise = require('bluebird');
Promise.promisifyAll( mongoose );
async syncGetMany(model, query) {
let ret;
try {
ret = await model.findAsync(query);
return ret;
} catch(err) {
console.error(err);
return []; //empty array or any error string/object
}
}
Third option (Node version => 8.0):
Using native promisify on function:
https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original
On our synchronous part (with async operator before function construction):
let data = await syncGetMany(MongooseModel, queryObj);
You can write and execute logic as if it was synchronous using nsynjs:
function synhronousCode(model, query) {
function syncGetMany(model, query){
return model.find(query).data; // data will contain result of resolved promise at this point
};
var data = syncGetMany(model, query);
// do stuff with data
}
var nsynjs = require('nsynjs');
....
nsynjs.run(synhronousCode,{},model, query, function(){
console.log('synhronousCode done');
});
nsynjs will evaluate code in synhronousCode in sequential manner: if some function returns promise (as model.find does), nsynjs will pause execution and wait until promise is resolved, assigns result to data property of returned value, and then continue execution.

NodeJS Express Async issue

I have this function which gets some data from my database but i'm having a trouble calling the function and getting the proper response
function getEvents()
{
var x = [];
var l = dbCollection['e'].find({}).forEach(function(y) {
x.push(y);
});
return x;
});
and another function which calls this function but it always returns undefined.
How can i make the function wait till mongoose has finished filling up the array?
Thanks for the help! My life
dbCollection['e'].find is called non-blocking way so you are returning x before filling. You need to use callbacks or some mongoose promises. You can get all returning values from database like following snippet
function getEvents(callback) {
dbCollection['e'].find({}, function(error, results) {
// results is array.
// if you need to filter results you can do it here
return callback(error, results);
})
}
Whenever you need to call getEvents function you need to pass a callback to it.
getEvents(function(error, results) {
console.log(results); // you have results here
})
You should read mongoose docs for how queries work.
There is also support for promises in mongoose. You can check this url for more information about promises.
The solution proposed by #orhankutlu should work fine.
I will give another solution using promise. You can choose one between these two solutions depending on your style of programming.
Solution using promise:
function getEvents() {
return new Promise(function(resolve, reject){
dbCollection['e'].find({}, function(error, results) {
if (error) return reject(error);
var x = [];
results.forEach(function(y){
x.push(y);
});
// forEach() is a blocking call,
// so the promise will be resolved only
// after the forEach completes
return resolve(x);
});
});
};
Calling getEvents():
getEvents().then(function(result){
console.log(result); //should print 'x'
}).catch(function(err){
// Handle error here in case the promise is rejected
});
I will encourage you to try both the approaches, ie, using callbacks and using promises. Hope you find it useful!

Mongodb iterate a collection of in a synchronous way using promises

I have a method (see code example) and I'm trying to return a promises list to parent method. My problem is when pushing promises in my promiseList array.
getDistance is a method returning a Promise. If I do .then() in getDistance method,everything is OK. Nevertheless, if I try to push the promise into my array, when I do .then in parent method, it is empty.
I think it may happen due to each loop is asynchronous...
var promiseList = [];
res = db.collection.find query statement...
res.each(function(err, doc) {
if (doc!==null) {
promiseList.push(
getDistance(orgLat, orgLon, destLat, desLon)
);
}
});
return Promise.all(promTimeList);
I'm using MongoDB as database, and NodeJS in server side, with mongodb as driver to connecto to my database and Bluebird as library to implement promises.
Thanks in advance!
I think you are correct in that the issue is due to the fact that each is asynchronous. You can use defer to wrap callback APIs (like each) into promises. It would work something like this:
res = db.collection.find query statement...
processResponse(res)
.then(function(promiseList) {
// execute all the promises
return Promise.all(promiseList);
}, function (err) {
// handle error
});
function processResponse(res) {
var deferred = Promise.pending();
var promiseList = [];
res.each(function(err, doc) {
if (err) {
// error, abort
deferred.reject(err);
return;
}
else if (doc != null) {
promiseList.push(
getDistance(orgLat, orgLon, destLat, desLon)
);
}
else {
// finished looping through all results, return the promise list
deferred.resolve(promiseList);
}
});
return deferred.promise;
}
See more on defer with bluebird at the following link (look for "So when should deferred be used?"):
https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns
Update: According to this post:
Define empty Bluebird promise like in Q
It looks like there is a Promise constructor that may be the preferred way to do this. The processResponse method would look like this:
function processResponse(res) {
return new Promise(function(resolve, reject) {
var promiseList = [];
res.each(function(err, doc) {
if (err) {
// error, abort
reject(err);
return;
}
else if (doc != null) {
promiseList.push(
getDistance(orgLat, orgLon, destLat, desLon)
);
}
else {
// finished looping through all results, return the promise list
resolve(promiseList);
}
});
});
}
Thanks to #Bergi. Please correct me if I am wrong. I am more familiar with the Q library (https://github.com/kriskowal/q) than bluebird.
Iterate the collection syncrhonously by using await with cursor.next() in a while loop:
var cursor = db.collection.find({});
while ((doc = await cursor.next()) != null) {
promiseList.push(
getDistance(doc.orgLat, doc.orgLon, doc.destLat, doc.desLon);
);
}

Node.js consecutive method calls with nested callback formatting

So I'm new to Node.js and Im just wondering if the way I have my code setup makes sense. Im coming from a Java background so the nested callback structure is new. I have a Node program that runs a bunch of code that I broke down into different methods. The thing is that the methods need to be called in order. My code has this structure right now:
functionOne(data, callback(err) {
functionTwo(data, callback(err) {
functionThree(data, callback(err) {
functionFour(data, callback(err) {
//Code
});
});
});
});
This is very minimalistic, but is this structure ok? With Java, I'd take the return values of all the methods, then just pass them to the next function. From my understanding so far, the Java approach I just mentioned is one of the main things that Node.js was trying to eliminate. But anyway... Does that structure look ok, and is that how its intended to look? Just want to be sure that I'm not making any major errors with Node in general. Thanks!
Your code structure looks fine if you work with callback pattern.
But if you're interested in make your code cleaner and readable you would like to use Promises in your asynchronous function, so instead of pass a callback to your functions you could do something like this :
function asyncFunction (data){
return new Promise(function(resolve, reject){
// Do something with data
// Here you can call reject(error) to throw an error
resolve();
});
}
And instead of nested function callbacks you can call then method of Promise.
asyncFunction(data)
.then(function(){
// Promise resolved
// Something has been done with data
});
With Promises you can also execute async fuctions in parallel :
Promise.all([asyncFunctionA(data), asyncFunctionB(data), asyncFunctionC(data)])
.then(function(){...});
EDIT
If you need to pass values of one function to another, your code should look like this :
asyncFunctionA(data)
.then(function(dataA){
return asyncFunctionB(dataA);
})
.then(function(dataB){
return asyncFunctionC(dataB);
})
.then(function(dataC){
// ...
});
You should try to use promises to avoid your callback hell, so it could be something like these...
const Q = require('q'); // you can do a research for this module.
var myModule = {};
myModule.functionOne = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.functionTwo = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.doAll = (params) => {
myModule.functionOne(params)
.then((outputFunctionOne) => {
// this is called after functionOne ends
return myModule.functionTwo(outputFunctionOne);
})
.then((outputFunctionTwo) => {
// this is called after function 2 ends
if (outputFunctionTwo.success) {
// if everything ok, resolve the promise with the final output
deferred.resolve(outputFunctionTwo);
} else {
// reject the promise with an error message
deferred.reject('error');
}
})
.fail((err) => {
// this is call if the promise is rejected or an exception is thrown
console.log(err); // TODO: Error handling
})
.done();
}
module.exports = myModule;
You can Chain as many promises as you want really easily, that way you get rid of the callback hell. Best part, you can do promises on Javascript or Node.js
Reference Link https://github.com/kriskowal/q
Hope this helps
Most of the other answers give Promise/A as the answer to your callback woes. This is correct, and will work for you. However I'd like to give you another option, if you are willing to drop javascript as your working language.
Introducing Iced Coffee, a branch of the CoffeeScript project.
With Iced Coffee you would write:
await functionOne data, defer err
await functionTwo data, defer err2
await functionThree data, defer err3
//etc
This then compiles to the CoffeeScript:
functionOne data, (err) ->
functionTwo data, (err2) ->
functionThree data, (err3) ->
//etc
Which then compiles to your Javascript.
functionOne(data, callback(err) {
functionTwo(data, callback(err2) {
functionThree(data, callback(err3) {
//etc
});
});
});

NodeJs Mongoose How can I get out a data from "find().then" in a "find().then"?

Sorry for my Title, I don't know what can I put.
Can you help me please, I would like to print data from a "then" in a "then" ?
Thank you
models.book.find()
.then( function (content) {
var i = 0;
while (content[i]) {
models.author.findOne({"_id": content[i].author_id}, function(err, data) {
console.log(data); //here, it' good
content[i] = data;
MY_DATA = content;
return MY_DATA;
});
i++;
};
})
.then(function (result) {
console.log(result); // here I would like to print MY_DATA
});
There are a number of problems with your code, and I don't think it's behaving as you're expecting it to.
Chaining Promises
In order to effectively chain promises how you're expecting, each promise callback needs to return another promise. Here's an example with yours changed around a bit.
var promise = models.book.find().exec(); // This returns a promise
// Let's hook into the promise returned from
var promise2 = promise.then( function (books) {
// Let's only get the author for the first book for simplicity sake
return models.author.findOne({ "_id": books[0].author_id }).exec();
});
promise2.then( function (author) {
// Do something with the author
});
In your example, you're not returning anything with your callback (return MY_DATA is returning within the models.author.findOne callback, so nothing happens), so it's not behaving as you're expecting it to.
model.author.findOne is asynchronous
model.author.findOne is asynchronous, so you can't expect to call it multiple times in the callback without handling them asynchronously.
// This code will return an empty array
models.book.find( function (err, books) {
var i = 0, results = [];
while (books[i]) {
models.author.findOne({ "_id": books[i].author_id}, function (err, data) {
// This will get called long after results is returned
results.push(data);
});
i++;
};
return results; // Returns an empty array
});
Handling multiple promises
Mongoose uses mpromise, and I don't see a method to handle multiple promises together, but here's a way your case could be done.
var Promise = require('mpromise');
models.book.find().exec()
.then( function (books) {
var i = 0,
count = 0,
promise = new Promise(),
results = [];
while (books[i]) {
models.author.findOne({ "_id": books[i].author_id }, function (err, author) {
results.push(author);
count++;
// Keep doing this until you get to the last one
if (count === books.length) {
// Fulfill the promise to get to the next then
return promise.fulfill(results);
}
return;
});
}
return promise;
})
.then( function (results) {
// Do something with results
});
I don't know if this will work exactly like it is, but it should give you an idea of what needs to be done.

Resources