node.js scope problems? - node.js

I'm working on making a script with node.js for setting up my dzen2 in i3, and haven't really used node for anything like this before.
I need the geometry of the screen to start with, which I can get with something like this:
geometry = getGeo();
function getGeo() {
var sh = require('child_process').exec("i3-msg -t get_outputs",
function(error, stdout, stderr) {
var out = JSON.parse(stdout);
return out[0].rect; //this is the geometry, {"x":0, "y":0, "width":1280, "height":768}
});
};
console.log(geometry);
console.log is logging undefined.
I'm not sure what the proper way to do this is, my brain is tired.

You can't return from the callback function since is async.
Rather write another function and pass the callback object to it.
function getGeo() {
var sh = require('child_process').exec("i3-msg -t get_outputs",
function(error, stdout, stderr) {
var out = JSON.parse(stdout);
getRect(return out[0].rect);
});
};
function getRect(rect) {
// Utilize rect here...
}

You never return a value from getGeo(), you're returning a function from the anonymous function within it. But because of the async nature of the .exec() call, you can't return the value. You can put the console.log into the callback function, but that may not be where you want to use it in your real program.

Related

Set variable equal to mongodb key value

var userLat = db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
console.log(docs.currentUserLat);
})
});
This is my code, I'm trying to get the value that's console logged into the variable. I just can't find the correct syntax to do this. The console log does return the correct value just need to drop it into the variable. Grateful for some help.
What do you want to do with 'docs.currentUserLat'?
You can do what you need to do without saving docs.currentUserLat to a variable that has scope outside of your db.collection call. Some examples:
If you simply want to change the document in your database, take advantage of the many methods specified in the Collections API: http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html. For example, to update the document and simultaneously resave it in the database:
db.collection('users', function (err, document){
document.findOneAndUpdate({_id: loggedUserID},
{currentUserLat: [updated value]},
function(err, docs) {
if(err) console.log(err);
}
)
});
If you just wanted to use docs.currentUserLat inside some node function, you'll need to properly nest the document.findOne function inside a callback (or vice versa). For example, to write currentUserLat to a file using the fs module:
var fs = require('fs');
db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
fs.writeFile("pathToYourFile", docs.currentUserLat, function(err) {
if(err) {return console.log(err);}
});
});
});
Or, if you want to send it in response to a simple http request:
var http = require('http');
http.createServer(function(request,response){
db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
response.writeHead(200,{'Content-Type':'text/html'});
response.end(docs.currentUserLat);
});
});
});
The key thing to remember is what JohnnyHK said in their comment: docs.currentUserLat is only available inside the anonymous function passed to findOne. So, whatever it is that you need to do, do it inside this function.
(Reading the link JohnnyHK provided is a great way to get started with understanding asynchronous functions in Node. Another is https://github.com/rvagg/learnyounode)
First of all you have to understand how javascript callback works. After that you will see that nothing assigns docs.currentUserLat to your userLat variable. The reason behind this is that your docs.currentUserLat is available only inside the callback. Think about it in the following way:
You program started to execute and encountered the line: var userLat = .... This line tells: do a callback (which basically asks someone else to do the job), your while your job is being executed the program continues, by assigning userLat to undefined and executes further. Then at some period of time callback finishes and console.log your docs.currentUserLat.
One way to have the desired behavior is to make userLat global and instead of console.log(docs.currentUserLat); do userLat = docs.currentUserLat. The problem that if you will do this, your userLat eventually will have the desired value (if callback will not fail), but you can not predict when. So if you will do
var userLat = db.collection('users', function (err, document){ ... });
.. some other code
console.log(userLat);
you will not be sure that you will get the output. Another way to do put everything in another callback.

Series flow with Async in NodeJS gets asynchrounously called

I need to call 3 functions in series x1() then x2() then x3(). But since x1 and x2 are time consuming operations x3 executes before them giving unexpected results. I have used the async library in NodeJS with the series method as below.
How can I solve this without using setTimeout for x3()
async.series([function(callback) { x1();callback(null, null);},
function(callback) { x2();callback(null, null);},
function(callback) {
x3();
callback(null, null); }],
function(err, results) { }
);
use recursive try catch..
a=true;
while(a){
try{
your work.....
a=false;
}catch(exp ){
a=true
}
}
You can use javascript's async nature to solve it. Here how it's done with callbacks. Call x3() instead of onMethodsDone().
Here, suppose x1() accepts an array. Forget it if you don't need any parameters. x3() gets the modified array from x2() which is also another time consuming function. setTimeout() is used to show the async behaviour since x1() and x2() are time consuming.
x1([1,2],function(results){
x2(results,onMethodsDone);
});
function onMethodsDone(results){
alert(results);
}
function x1(records,cb){
setTimeout(function (){
records.push(3,4,5);
cb(records); //parse updated records to callback
},2000);
}
function x2(records,cb){
setTimeout(function (){
records.push(6,7,8);
cb(records);
},2000);
}
javascripts Listeners or Promise/defers also can be used to handle it.
As the callback function had to be overloaded with different number of arguments the best option was to structure the method as follows
function callback(cbargs){
var arg1 = cbargs.cbarg1;
var arg2 = cbargs.cbarg2;
if(arg3!=null){
.....
}
}
function x1(arg1 , arg2, callback, cbargs){
.....
.....
callback(cbargs);
}
function x3(arg1 , arg2, callback, cbargs){
.....
.....
callback(cbargs);
}
x1(arg1, arg2,callback,{cbarg1, cbarg2});
x1(arg1, arg2,callback,{cbarg1, cbarg2, cbarg3});

Node.js: Continuous execution from settimeout when expecting node to do nothing until settimeout interval over

I'm writing a notifier for 3 deal of the day websites in node. I go and parse the body of the webpage to grab the details. In the details there is a timer for the how long the deal will last. I'm reading that timer and trying to use setTimeout/setInterval to set when the function should execute again. However the function calls are continuous instead of waiting.
Pseudo code of what I'm doing:
var getData = function(url) {
request(url, function(err, resp, body){
if(err) throw err;
//process the body getting the deal information and timer
setTimeout(getData(url),timer*1000);
}
getData(url1);
getData(url2);
getData(url3);
Full code here.
I want the program to run, continually calling itself with the new timeouts for the webpages.
I'm a Node.js newbie so I'm guessing I'm getting tripped up with the async nature of things.
Any help is greatly appreciated.
EDIT:
more simply :
var hello = function(){
console.log("hello");
setTimeout(hello(),25000);
}
hello();
prints out hello continuously instead of hello every 2.5s. What am I doing wrong?
The problem is evident in your hello example, so lets take a look at that:
var hello = function(){
console.log("hello");
setTimeout(hello(),25000);
}
hello();
In particular this line: setTimeout(hello(),25000);. Perhaps you are expecting that to call hello after a 25 second timeout? Well it doesn't, it calls hello immediately, (that's what hello() does in Javascript, and there is nothing special about setTimeout), and then it passes the return value of hello() to setTimeout, which would only make sense if hello() returned another function. Since hello recursively calls itself unconditionally, it doesn't ever return, and setTimeout will never be called. It's similar to doing the following:
function hello() {
return doSomething(hello());
}
Is it clear why doSomething will never be called?
If you want to pass a function to setTimeout, just pass the function itself, don't call it and pass the return value: setTimeout(hello, 25000);.
Your fixed code:
var getData = function(url) {
request(url, function(err, resp, body){
if(err) throw err;
//process the body getting the deal information and timer
setTimeout(getData, timer*1000, url);
});
};
getData(url1);
getData(url2);
getData(url3);
Noticed that I passed the argument for getData as a third argument to setTimeout.
What's happening is 'request' is being run as soon as getData is called. Do you want getData to be the function you call to start the timer, or the one that loads the data?
var getData = function(url) {
function doRequest(url) {
request(url, function(err, resp, body) {
if(err) throw err;
//process the body getting the deal information and timer
}
setTimeout(doRequest(url),timer*1000);
}
getData(url1);
getData(url2);
getData(url3);
What you want is 'setTimeout' to point to a function (or anonymous function/callback) that you run after the timer expires. As you originally wrote, getData was immediately calling request (and then calling getData again after your timer)

node is it possible to do if statements on events?

Kind of like this?
if (this.on('pipe',function () {
return true
}){
//do something if piped
}else{
//do something if not piped
}
Because I want to do something different depending on whether or not something is piped to the function or not.
I'm guessing that won't work however because of how async node is, but I need something like that at least.
The main thing is I need something to run either OR but not both, if I were to implement a callback it won't work exactly like I want it to because whatever code I put after the this.on will still run, i need it to not run if this.on fires.
EDIT: Would process.nextTick() be what I need?
I can't immagine the real situation where it's needed. I think you try to do something like this:
function mainContextFunction() {
var isPiped = false;
this.on('pipe', function(){isPiped = true;});
function delayedExecution() {
if (isPiped) ...;
else ...
}
setTimeout(delayedExecution, 1000);
}
mainContextFunction();
Ok, what I needed to do was Defer it to the next stack process with process.nextTick() so that it executed AFTER the .on callback was executed. so it's like this:
upload = function (path,file,callback){
this.notpiped = true
this.on('pipe',function () {
this.notpiped = false
})
process.nextTick(function() {
if(this.notpipe){
cback(path,file,callback)
}
})
this.end = function () {
//handle piped data, eg, parse it, or set it or whatever and then use cback
}
}
upload.prototype._write = (chunk, encoding, callback){
this.data = this.data + chunk
}
so now this .end will fire like normal ONLY if .pipe gets called on it BUT if pipe doesn't get called then .end will never fire so it's ok to do it like this.
I can now have a function that can have data streamed to it, OR directly used depending on circumstances.
My actual function I'm using does a bit more then this and looks a little different but the concept is the same.

Async.js Parallel Callback not executing

I'm working with the parallel function in Async.js and for some reason the final call back is not getting executed and I do not see an error happening anywhere.
I'm dynamically creating an array of functions that are passed to the parallel call as such:
// 'theFiles' is an array of files I'm working with in a code-generator style type of scenario
var callItems = [];
theFiles.forEach(function(currentFile) {
var genFileFunc = generateFileFunc(destDir + "/" + currentFile, packageName, appName);
callItems.push(genFileFunc(function(err, results) {
if(err) {
console.error("*** ERROR ***" + err);
} else {
console.log("Done: " + results);
}
}));
});
async.parallel(callItems, function(err, results) {
console.log(err);
console.log(results);
if(err) {
console.error("**** ERROR ****");
} else {
console.log("***** ALL ITEMS HAVE BEEN CALLED WITHOUT ERROR ****");
}
});
Then in an outside function (outside of the function that is executing the forEach above) I have the generateFileFunc() function.
// Function that returns a function that works with a file (modifies it/etc).
function generateFileFunc(file, packageName, appName) {
return function(callback) {
generateFile(file, packageName, appName, callback);
}
}
I've looked at this SO post and it helped me get to where I'm at. However the final call back is not being executed. All of the items in the parallel call are being executed though. Inside of gnerateFile (function) at the very bottom I call the callback, so thats golden.
Anyone have any idea why this might not be executing properly?
The end result is to work with each function call in parallel and then be notified when I'm done so I can continue executing some other instructions.
Thanks!
Analyze what is happening line by line, starting with this:
var genFileFunc = generateFileFunc(...);
Since your function generateFileFunc returns function, so variable genFileFunc is a following function
genFileFunc === function(callback) {
generateFile( ... );
};
Now it is clear that this function returns nothing ( there is no return statement ). And obviously by nothing I understand JavaScript's built-in undefined constant. In particular you have
genFileFunc(function(err, results) { ... } ) === undefined
which is the result of calling it. Therefore you push undefined to callItems. No wonder it does not work.
It is hard to tell how to fix this without knowing what generateFile exactly does, but I'll try it anyway. Try simply doing this:
callItems.push(genFileFunc);
because you have to push function to callItems, not the result of the function, which is undefined.
Curious.
Best guess so far: Inside generateFile, RETURN callback instead of calling it.
You can achieve the stated goal with
async.map(theFiles, function(file, done) {
generateFile(destDir + "/" + file, packageName, appName, done);
}, function(err, res) {
// do something with the error/results
});

Resources