Search code examples
eventsnode.jsscoping

How do I emit to an eventListener from inside a nested function in Node.js (javascript scoping issue)


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);
            });
        };
    }

Solution

  • 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);
        });
    };