I am writing code below that parses a sites API one at a time, than tells an event queue it is ready for the next object to parse. I am having issues since I am still new to javascript scoping, and would like to emit from SiteParser or call the emitForNext function. I cannot seem to bring emitForNext into scope in the error callback.
function SiteParser(){
this.emitForNext = function (message) {
this.emit("next", message);
};
this.pullJSON = function (path, processJSON) { //processJSON is a callback function
var options = {
host: 'www.site.com',
port: 80,
path: path
}
//console.log("... processing "+path);
//pulls the entire json request via chunks
http.get(options, function (res) {
var resJSON = ''; //stores the comment JSON stream given in the res
res.on('data', function (chunk) {
resJSON+=chunk;
});
res.on('end', function () {
var obJSON = (JSON.parse(resJSON));
if (obJSON.hasOwnProperty("error")){
console.log(obJSON);
console.log('... ', path, ' does not exist');
//
//NEED A NEXT EVENT EMMITER HERE NEED TO FIGURE OUT SCOPE
//
//
} else {
processJSON(obJSON); //call the callback function
}
}) ;
}).on('error', function (e) {
emitForNext("got error: " + e.message);
});
};
}
JavaScript has function scoping, if you declare a variable with the var keyword, it will be local to the current function. When you access a variable, it will look to the scope chain which consist of the current function, it's parent function, …. Try:
function one() {
var foo = 'foo';
function two() {
console.log(foo) // undefined. I'll explain this
var foo = 'bar';
console.log(foo) // bar
}
two()
console.log(foo) // foo
}
one()
Most of the time we define variables at the beginning of functions, because a variable defined in function's body get hoisted. Basically, it means that it's available in the whole function, even before it's defined but in this case it's value is undefined
.
For example if a variable is not defined we normally get a ReferenceError
, but in the snippet below, both console.log()
just print undefined
.
function foo() {
console.log(bar);
if (0) {
var bar = 'bar';
}
console.log(bar);
}
So, a common practice is that, when you write long functions, you map this to self.
function SiteParser() {
var self = this;
// ...
.error('error', function(err) {
self.emitForNext("got " + err.message);
})
}
You should not write all your methods in the constructor, it's only usefull sometimes when we want privacy, but in this case you'd better use prototypes.
Putting this together, I would write:
var SiteParser = function() {};
SiteParser.prototype.emitForNext = function(message) {
this.emit("next", message);
};
SiteParser.prototype.pullJSON = function(path, processJSON) {
var self = this,
options = {
host: 'www.site.com',
port: 80,
path: path
};
http.get(options, function(res) {
// ...
}).on('error', function (e) {
self.emitForNext("got error: " + e.message);
});
};