Passing variable from function to outside the function - node.js

I'm trying to get a variable (listID) passed from the function to outside the function and I'm getting the result as undefined. I see this requires a callback, but I'm not clear on how to do this (I'm new to nodejs).
Can anybody show me how this would be done? Thanks.
twitter.get('/lists/list', function(err, data, response) {
for (var i = 0; i < data.length; i++) {
var j=i+1;
if (j==number){
var listID=data[i].id;
}
}
});
console.log(listID);

Related

How to cache outer scope values to be used inside of Async NodeJS calls?

Something like the below code illustrates my intention, if you can imagine how a naive programmer would probably try to write this the first time:
function (redisUpdatesHash) {
var redisKeys = Object.keys(redisUpdatesHash);
for (var i = 0; i < redisKeys.length; i++) {
var key = redisKeys[i];
redisClient.get(key, function (err, value) {
if (value != redisUpdatesHash[key]) {
redisClient.set(key, redisUpdatesHash[key]);
redisClient.publish(key + "/notifications", redisUpdatesHash[key]);
}
});
}
}
The problem is, predictably, key is the wrong value in the callback scopes of the asynchronous nature of the node_redis callbacks. The method of detection is really primitive because of security restrictions out of my control - so the only option for me was to resort to polling the source for it's state. So the intention above is to store that state in Redis so that I can compare during the next poll to determine if it changed. If it has, I publish an event and store off the new value to update the comparison value for the next polling cycle.
It appears that there's no good way to do this in NodeJS... I'm open to any suggestions - whether it's fixing the above code to somehow be able to perform this check, or to suggest a different method of doing this entirely.
I solved this problem through using function currying to cache the outer values in a closure.
In vanilla Javascript/NodeJS
asyncCallback = function (newValue, redisKey, redisValue) {
if (newValue != redisValue) {
redisClient.set(redisKey, newValue, handleRedisError);
redisClient.publish(redisKey + '/notifier', newValue, handleRedisError);
}
};
curriedAsyncCallback = function (newValue) {
return function (redisKey) {
return function (redisValue) {
asyncCallback(newValue, redisKey, redisValue);
};
};
};
var newResults = getNewResults(),
redisKeys = Object.keys(newResults);
for (var i = 0; i < redisKeys.length; i++) {
redisClient.get(redisKeys[i], curriedAsyncCallback(newResults[redisKeys[i]])(redisKeys[i]));
}
However, I ended up using HighlandJS to help with the currying and iteration.
var _ = require('highland'),
//...
asyncCallback = function (newValue, redisKey, redisValue) {
if (newValue != redisValue) {
redisClient.set(redisKey, newValue, handleRedisError);
redisClient.publish(redisKey + '/notifier', newValue, handleRedisError);
}
};
var newResults = getNewResults(),
redisKeys = Object.keys(newResults),
curriedAsyncCallback = _.curry(asyncCallback),
redisGet = _(redisClient.get.bind(redisClient));
redisKeys.each(function (key) {
redisGet(key).each(curriedAsyncCallback(newResults[key], key));
});

NodeJs giving me a Object #<Object> has no method

I have a class along with its helper class defined:
function ClassA(){
this.results_array = [];
this.counter = 0;
this.requestCB = function(err, response, body){
if(err){
console.log(err);
}
else{
this.counter++;
var helper = new ClassAHelper(body);
this.results_array.concat(helper.parse());
}
};
};
function ClassAHelper(body){
this._body = body;
this.result_partial_array = [];
this.parse = function(){
var temp = this.parseInfo();
this.result_partial_array.push(temp);
return this.result_partial_array;
};
this.parseInfo = function(){
var info;
//Get some info from this._body
return info
};
};
NodeJS gives me the following error:
TypeError: Object #<Object> has no method 'parseInfo'
I cannot figure out why I can't call this.parseInfo() from inside ClassAHelper's parse method.
If anyone can explain a possible solution. Or at least, what is the problem? I tried reordering the function declarations, and some other ideas, but to no avail.
P.S. I tried simplifying the code for stackoverflow. Hepefully it still makes sense :)
P.P.S This is my first stackoverflow question. Hopefully I did everything right. :)
Here's a simplified example which works:
function A() {
this.x = function (){
return this.y();
};
this.y = function (){
return "Returned from y()";
};
}
var a = new A();
a.x();
Notice the use of new and calling the method with a.x().
How are you creating an instance of your functions and calling parse in ClassAHelper?
Is it anything like these:
var a = A();
a.x();
// Or
A.x()
this is scoped to the function it is inside. So, when you do this.parse=function(){, there is a new this. To keep ClassAHelper's this, you have to pass it in or reference it inside the anonymous function you made. The following example assigns this to a variable outside of the function and references it inside the function:
function ClassAHelper(body){
this._body = body;
this.result_partial_array = [];
var self = this;
this.parse = function(){
var temp = self.parseInfo();
self.result_partial_array.push(temp);
return self.result_partial_array;
};
this.parseInfo = function(){
var info;
//Get some info from this._body
return info;
};
};
Further reading and other ways of doing it:
Why do you need to invoke an anonymous function on the same line?

turning a synchronous function into asynchronous function

I have a for loop which does many iterations .I would like to put that piece of code in a custom async function as it is blocking.Is there anyway I can write a function so it will call a callback once the loop iteration is over?.
Use asynchronous-function-inside-a-loop paradigm. This ensures that the asynchronous functions get called with the correct value of the index variable.
var total = someObject.list.length;
var count = 0;
for(var i = 0; i < total; i++){
(function(foo){
myobj.get(someObject.list[foo], function(err, response) {
do_something(foo);
count++;
if (count > total - 1) done();
});
}(i)); //To immediately invoke the function passing 'i' as parameter
}
function done() {
console.log('All data loaded');
}

Error on callback when running a recursion function using async

I try to execute a recursion through a tree, in order to exec node_func for each node in the tree. node_func also returns the next values in the tree under [values].
I use async.eachSeries which get a list of the nodes in the next level of the tree.
The function runs successfully over the first branch of the tree, but at the leaf where I have the stop condition, I try to call the callback but it's undefined.
The code:
function clone(a) {
return JSON.parse(JSON.stringify(a));
}
var searchNext = function(params, callbackSN){
var seParams = clone(params);
node_func(seParams,function(searchRes){
//Stop Condition - return
if (searchRes["nextFeature"] != 1){
return callbackSN(); //Stop Condition
}
var values = searchRes["values"]
var paramsArr = []
for (var i = 0; i < values.length; i++) {
var seParams2 = clone(seParams);
seParams2["value"].push(values[i]["value"])
paramsArr.push(seParams2)
};
async.eachSeries(paramsArr, searchNext, function(err){
return callbackSN(err)
});
})
}
//init search
var params = {"value" :[]}
searchNext(params,console.log)
When I run it, it runs over the first branch, and when it gets to the "Stop Condition" I get the following error:
TypeError: undefined is not a function
Pointing to the line:
return callbackSN(); //Stop Condition
At the Stop Condition
In the line
return callback(err)
You are invoking the callback function, but it is not defined in your code. I guess you want to call the callbackSN function.
return callbackSN(err)
I had an error in the original code :
function clone(a) {
return JSON.parse(JSON.stringify(a));
}
var searchNext = function(params,db, callbackSN){
var seParams = clone(params);
node_func(seParams,db,function(searchRes){
//Stop Condition - return
if (searchRes["nextFeature"] != 1){
return callbackSN(); //Stop Condition
}
var values = searchRes["values"]
var paramsArr = []
for (var i = 0; i < values.length; i++) {
var seParams2 = clone(seParams);
seParams2["value"].push(values[i]["value"])
paramsArr.push(seParams2)
};
async.eachSeries(paramsArr, searchNext, function(err){
return callbackSN(err)
});
})
}
//init search
var params = {"value" :[]}
searchNext(params,console.log)
The second variable "db" at node_func cannot be called from the async, so it made the confusion.
I added the "db" variable at the parent function as a local variable.

adding a js method

I have a js function that is named getID which is basically return document.getElementById(id)
I want to make another function, getTag that would return getElementsByTagName.
The part that I can't seem to manage is that I want to be able to call them like this:
getID('myid').getTag('input') => so this would return all the input elements inside the element with the id myid
Thanks!
ps: getTag would also have to work if it's called by it's own, but then it would just return document.getElementsByTagName
UPDATE:
Thanks to all that have replied! Using your suggestions I came up with this, which works well for me:
function getEl(){
return new getElement();
}
function getElement() {
var scope = document;
this.by = function(data){
if (data.id) scope = scope.getElementById(data.id);
if (data.tag) scope = scope.getElementsByTagName(data.tag);
return scope;
}
}
and I use it like this:
var inputs = getEl().by({id:"msg", tag:"input"});
The way to do that is to prototype Object. To do that, you'll need the following piece of code:
Object.prototype.getTag = function(tagName) {
return this.getElementsByTagName(tagName);
}
However, this will expand all objects because what you really need to prototype, an HTMLElement, is very hard to do consistently. All the experts agree that you should never expand the Object prototype. A much better solution would be to create a function that gets the tag name from another argument:
function getTag(tagName, element) {
return (element || document).getElementsByTagName(tagName);
}
// Usage
var oneTag = getTag('input', getID('myid')); // All inputs tags from within the myid element
var twoTag = getTag('input'); // All inputs on the page
This would require that whatever is returned by getID('myid') (an HTML element) exposes a method named getTag(). This is not the case. Browsers implement the DOM specification and expose the methods defined there.
While you technically can enhance native objects with your own methods, it's best not to do it.
What you try to do has been solved rather nicely in JS libraries like jQuery already, I recommend you look at one of them before you invest time in mimicking what they can do. For example, your line of code would become:
$("#myid input")
in jQuery. jQuery happens to be the most widely used JS library around, there are many others.
Basically, you're going to create a single object that contains each of your methods and also stores all data returned by the native functions. It would look something like this (not tested, but you get the idea):
var MyLib = {
getID: function(id) {
var element = document.getElementById(id);
this.length = 1;
this[0] = element;
return this;
},
getTag: function(tag) {
var elements;
if (this.length) {
for (var i = 0; i < this.length; i++) {
var byTag = this[i].getElementsByTagName(tag);
for (var j = 0; j < byTag.length; j++) {
elements.push(byTag[j]);
}
}
}
for (var i = 0; i < elements.length; i++) {
this[i] = elements[i];
}
this.length = elements.length;
return this;
}
};
You can then use it like this:
var elements = MyLib.getID('myid').getTag('input');
for (var i = 0; i < elements.length; i++)
console.log(elements[i]); // Do something
The only real problem with this approach (besides it being very tricky and hard to debug) is that you have to treat the result of every method like an array, even if there is only a single result. For example, to get an element by ID, you'd have to do MyLib.getID('myid')[0].
However, note that this has already been done before. I recommend you take a look at jQuery, if only to see how they accomplished this. Your code could be simplified to this:
$("#myid input")
jQuery is more lightweight than you think, and including it on your page will not slow it down. You have nothing to lose by using it.
Just use the DOMElement.prototype property.
You'll get something like this :
function getTag(tagName) {
return document.getElementsByTagName(tagName);
}
DOMElement.prototype.getTag = function(tagName) {
return this.getElementsByTagName(tagName);
}
But you should use jQuery for this.
EDIT: My solution doesn't work on IE, sorry !
You could define it as follows:
var Result = function(el)
{
this.Element = el;
};
Result.prototype.getTag = function(tagName)
{
return this.Element.getElementsByTagName(tagName);
};
var getTag = function(tagName)
{
return document.getElementsByTagName(tagName);
};
var getID = function(id)
{
var el = document.getElementById(id);
return new Result(el);
};
Whereby a call to getID will return an instance of Result, you can then use its Element property to access the HTML element returned. The Result object has a method called getTag which will return all child elements matching that tag from the parent result. We then also define a seperate getTag method which calls the document element's getElementsByTagName.
Still though...JQuery is so much easier... $("#myId input");
Unless this is an academic exercise on how to chain methods in JavaScript (it doesn't seem to be, you simply seem to be learning JavaScript), all you have to do is this:
var elements = document.getElementById("someIdName");
var elementsByTag = elements.getElementsByTagName("someTagName");
for (i=0; i< elementsByTag.length; i++) {
alert('found an element');
}
If you want to define a reusable function all you have to do is this:
function myFunction(idName,tagName) {
var elements = document.getElementById(idName);
var elementsByTag = elements.getElementsByTagName(tagName);
for (i=0; i< elementsByTag.length; i++) {
alert('found a ' + tagName + ' element within element of id ' + idName);
}
}
It's true that if this is all the JavaScript functionality you need on your page, then there is no need to import jQuery.

Resources