Promise inside loop in nodejs isn't working as expected - node.js

I have array of events, for each value there may/may not query fires to get data.
var eventTypes = [["write","write","write","write"],["write","write","read","write"]];
_.each(eventTypes,function(obj){
gettime(obj);
});
gettime=function(events){
var resultArray = [];
_.each(events,function(event){
if(event === "read"){
resultArray.push(makesqlquery(event));
}else{
resultArray.push({"time":current_time})
}
});
q.all(resultArray).then(function(finalResult){
insertIntoPostgreSQL(finalResult);
});
}
makesqlquery = function(event){
var deferred = q.defer();
sql.query("select time from events where eventtype ="+ event,
function(result,error){
if(error){
deferred.reject(error);
}else{
deferred.resolve({time:result.time});
}
});
return deferred.promise;
}
In the above code I'm able to push 1st set of data(["write","write","write","write"]) into postgresql database but not 2nd set(["write","write","read","write"]). Whenever I get read event in a set, I'm getting empty object. What would be the problem? For the above example I should have 8 records in postgresql, but I see only first array's four data.
More Info: insertIntoPostgreSQL() function will get list of objects and insert each object into database. This operation is workin fine.
I tried use two console stmt as
console.log("sql result:"result);
deferred.resolve({time:result.time});
and
console.log("Before Insert:"JSON.stringigy(resultArray));
q.all(resultArray).then(function(finalResul‌​t){
I get result as in the following order.
Before insert:[{"source":{}},{"source":{}},{"source":{}},{"source":{}}]
Before insert:[{}]
sql result:{time:"2015-07-10 00:00:00"}

As Roamer-1888 mentioned in the comments, definitely add an error handler for your Q.all. The basic structure of your promise seems to be fine, the error is somewhere else.
It looks like the result in your sql.query callback is not quite what you expect, as it is read as undefined. Because of an error there, Q.all is not getting resolved, therefore nothing gets added to your database.

Related

Node.js Firestore forEach collection query cannot populate associative array

In this simplified example, associative array A cannot be populated in a Node.js Firestore query---it's as if there is a scoping issue:
var A = {};
A["name"] = "nissa";
firestore.collection("magic: the gathering")
.get()
.then(function(query) {
query.forEach(function(document) {
A[document.id] = document.id;
console.log(A);
});
})
.catch(function(error) {
});
console.log(A);
Console output:
{ name: 'nissa' } < last console.log()
{ name: 'nissa', formats: 'formats' } < first console.log() (in forEach loop)
{ name: 'nissa', formats: 'formats', releases: 'releases' } < second console.log() (in forEach loop)
Grateful for any assistance, please request for further detail if needed.
Data is loaded from Firestore asynchronously, and while that is happening, your main code continues to run.
It's easiest to see what that means by placing a few logging statements:
console.log("Starting to load data");
firestore.collection("magic: the gathering")
.get()
.then(function(query) {
console.log("Got data");
});
console.log("After starting to load data");
When you run this code, it prints:
Starting to load data
After starting to load data
Got data
This is probably not the order that you expected the logging to be in. But it is actually working as intended, and explains the output you see. By the time your last console.log(A); runs, the data hasn't been loaded yet, so A is empty.
The solution is simple, but typically takes some time to get used to: all code that needs the data from the database must be inside the callback, or be called from there.
So something like this:
var A = {};
A["name"] = "nissa";
firestore.collection("magic: the gathering")
.get()
.then(function(query) {
query.forEach(function(document) {
A[document.id] = document.id;
});
console.log(A);
})
Also see:
Array of JSON object is not empty but cannot iterate with foreach, show zero length
NodeJS, Firestore get field
Unable to add Google markers inside a loop, a more complex problem, calling multiple asynchronous API
scope issue in javascript between two Functions, which also shows using the more modern async and await keywords, instead of then()
How to get data from firestore DB in outside of onSnapshot, which uses an onSnapshot listener instead of get()

Using Mongodb variables out of its functions

So I'm making a web application and I'm trying to send variables to an EJS file but when they are sent out of the mongo functions they come out as undefined because it's a different scope for some reason. It's hard to explain so let me try to show you.
router.get("/", function(req, res){
var bookCount;
var userCount;
Books.count({}, function(err, stats){
if(err){
console.log("Books count failed to load.");
}else{
bookCount = stats;
}
});
User.count({}, function(err, count){
if(err){
console.log("User count failed to load.")
}else{
userCount = count;
console.log(userCount);
}
});
console.log(userCount);
//Get All books from DB
Books.find({}, function(err, allbooks){
if(err){
console.log("Problem getting all books");
}else{
res.render("index", {allbooks: allbooks, bookCount: bookCount, userCount: userCount});
}
});
});
So in the User.Count and Books.count I'm finding the number of documents in a collection which works and the number is stored inside of the variables declared at the very top.
After assigning the numbers like userCount i did console.log(userCount) which outputs the correct number which is 3, If was to do console.log(userCount) out of the User.count function it would return undefined, which is a reference to the declaration at the very top.
What is really weird is that Book.Find() has the correct userCount even though its a totally different function. The whole goal im trying to accomplish is doing res.render("index", {userCount: userCount}); outside of the Books.find(). I can do it but of course for some reason it passes undefined instead of 3. I hope this made a shred of sense.
I seem to have found a solution. but if anyone knows a different way I would love to know. So basically all you need to do is move the User.Count function outside of the router.get() function. Not completely sure about the logic of that but it works...
This is a classic asynchronous-operation problem: Your methods (Books.count, Books.find, User.count) are called immediately, but the callback functions you pass to them are not. userCount is undefined in your log because console.log is called before the assignment in the callback function is made. Your code is similar to:
var userCount;
setTimeout(function() {
userCount = 3;
}, 1000);
console.log(userCount); // undefined
User.count takes time to execute before calling back with the result, just like setTimeout takes the specified time to execute before calling its callback. The problem is JS doesn't pause and wait for the timeout to complete before moving on and calling console.log below it, it calls setTimeout, calls console.log immediately after, then the callback function is called one second later.
To render a complete view, you need to be sure you have all of the data before you call res.render. To do so you need to wait for all of the methods to call back before calling res.render. But wait, I just told you that JS doesn't pause and wait, so how can this be accomplished? Promise is the answer. Multiple promises, actually.
It looks like you are using Mongoose models. Mongoose has been written so that if you don't pass a callback function to your methods, they return a promise.
Books.count({}) // returns a promise
JS promises have a method then which takes a callback function that is called when the promise has been resolved with the value of the asynchronous method call.
Books.count({}) // takes some time
.then(function(bookCount) { // called when Books.count is done
// use the bookCount here
})
The problem is, you want to wait for multiple operations to complete, and multiple promises, before continuing. Luckily JS has a utility just for this purpose:
Promise.all( // wait for all of these operations to finish before calling the callback
Books.count({}),
User.count({}),
Books.find({})
)
.then(function(array) { // all done!
// the results are in an array
bookCount = array[0];
userC0unt = array[1];
allBooks = array[2];
})

How do I ensure db call is completed before using db.colse() [in node.js to access mongo.db]

var storedArticleArray = db.collection('storedArticle').find(query).toArray;
console.dir(storedArticleArray);
db.close();
How can I ensure that console.dir(stroedArticleArray) displays its argument only after the database completed the query and stored the result in storedArticleArray?
Also db.close() does not close before the query is completed.
Does this work:
var storedArticleArray = db.collection('').find(query).toArray(function() {
console.dir(storedArticleArray);
db.close();
});
You must use Node.js callback functions when you query your MongoDB database, because they're asynchronous operations.
Following your example you can use:
var storedArticleArray = [];
db.collection('storedArticle').find(query, function(error, data) {
storedArticleArray = data.toArray;
console.dir(storedArticleArray);
db.close();
});
The callback function will be executed once the query will be completed and returned data (or an error, that you must always handle). In the callback you'll be sure to close the db connection without problems.

Returned method call is undefined?

Ok so i am using a method to make a request and pull some tables from another URL
Meteor.methods({
gimmetitle: function () {
var url = 'http://wiki.warthunder.com/index.php?title=B-17G_Flying_Fortress';
request(url, function(err, response, body) {
$ = cheerio.load(body);
var text = $('.flight-parameters td').text();
console.log(text);
return text;
});
}
});
When called the td's in the table succesfully print to the server console: http://prntscr.com/721pjh
Buuut, when that text is returned from that method to this client code, undefined is printed to the console:
Template.title.events({
'click #thebutton': function () {
Meteor.call('gimmetitle', function(error, result){
Session.set('gogle', result);
});
var avar = Session.get('gogle');
console.log(avar);
}
});
Ideas?
You need to understand two different things here :
On the client side, making some calls to the server is always asynchronous, because we have to deal with network latency. That's why we use callbacks to fetch the result of Meteor methods : this code is executed some time in the future, not right away.
This is why Session.set('gogle', result); is actually executed AFTER var avar = Session.get('gogle'); even though it appears before in your event handler code flow.
Contrary to template helpers, event handlers are NOT reactive, so it means that when you set the Session variable to the result of the method, the event handler code is not automatically reexecuted with the new value of Session.get('gogle').
You'll need to either do something with the result right in the Meteor method callback, or use a reactive computation (template helpers or Tracker.autorun) depending on Session.get('gogle') to rerun whenever the reactive data source is modified, and use the new value fetched from the server and assigned to the Session variable.
Quick update..Was able to fix this with just 1 line of code lol.
instead of request(url, function(err, response, body) i used the froatsnook:request package and used var result = request.getSync(url, {encoding: null}); and then just replaced $ = cheerio.load(body); with $ = cheerio.load(result.body);.

NodeJS render result of MySQL query properly

I got a MySQL query where I ask for the values in a specific field with the help of the SELECT statement. My question is: How do I get the value of the query ? When I render the template with my current code I receive following output on my page [object Object]. I have the following Code:
var network;
function query(sql, callback) {
connection.query(sql, function(err, rows) {
if (err) {
callback(err, null);
} else {
callback(rows);
res.render('index.hjs', { Mysql : network });
console.log(network);
}
});
}
query("SELECT testString FROM test", function(results){
network = results;
});
There are a couple of corrections in your code. Nothing wrong but just good practice. In error callback, you are sending two arguments but in actual query callback function, you are using only one argument.
Second thing is that res will be undefined inside query function. (Of course, unless you have it inside request handler, which spoils the purpose of having it as a function)
Without your database structure and code structure, this is the best I can do to help you.

Resources