I am learning node.js. On the nodejs api website there is a piece of code that I don't really get.
The link is here
var util = require("util");
var events = require("events");
function MyStream() {
events.EventEmitter.call(this);
}
util.inherits(MyStream, events.EventEmitter);
MyStream.prototype.write = function(data) {
this.emit("data", data);
}
var stream = new MyStream();
console.log(stream instanceof events.EventEmitter); // true
console.log(MyStream.super_ === events.EventEmitter); // true
stream.on("data", function(data) {
console.log('Received data: "' + data + '"');
})
stream.write("It works!"); // Received data: "It works!"
so the confusing part is
events.EventEmitter.call(this);
What does it do here?
MyStream is a new object declaration that inherits behaviors from events.EventEmitter as can be seen from this line where the inheritance is configured:
util.inherits(MyStream, events.EventEmitter);
So, when the MyStream constructor is invoked usually via something like var stream = new MyStream();, it needs to also invoke the constructor of the object that it inherits from so the parent object can initialize itself properly. That's what this line is:
events.EventEmitter.call(this);
events.EventEmitter is the constructor of the object that MyStream inherits from. events.EventEmitter.call(this) instructs Javascript to call that constructor with the this pointer set to this object.
If you need more help with understanding .call(), you can read this MDN reference.
Related
I am working on a node.js application with postgresql, using the express framework. I am trying to follow MVC as much as possible.
I want to generate query results in a model class and then pass them to a controller class. That controller class is actually defined in the routes, so that controller class can take the results and pass them as http response.
This is my database helper class, i.e. the model class. My problem is at the listener at the very end of the class.
exports.DatabaseHelper = function()
{
var allVenues;
var client;
var customEventEmitter;
this.init = function()
{
this.customEventEmitter = new events.EventEmitter();
client = new pg.Client(
{
host:'localhost',
port:5432,
database:'postgres',
user:'postgres',
password:'password'
});
}
this.getVenuesWithEvents = function(searchParams)
{
allVenues = new Array();
var query_for_venues;
this.init();
client.connect();
client.addListener("error",function()
{
sys.puts("postgresql interface error");
});
query_for_venues = client.query("select id, name, location, latitude, longitude, category_generalized from venues");
query_for_venues.addListener("row",function(row)
{
//some code
});
query_for_venues.addListener("end",function(result)
{
this.customEventEmitter.emit("data",allVenues);
//////////////////////////////////////////////////////////
//this line shows error....'this' refers to the query object so customEventEmitter is undefined
//customEventEmitter is my idea of sharing the query results to my controller class.
//but I cannot do this becasue of this error
console.log("after emission");
});
}
}
How can I access the customEventEmitter instance variable from within the listener?
Just remove this from your init function:
this.customEventEmitter = new events.EventEmitter();
So you'll have:
customEventEmitter = new events.EventEmitter();
And in your listener just emit the emitter without this as follows:
query_for_venues.addListener("end",function(result){
customEventEmitter.emit("data",allVenues);
console.log("after emission");
});
let me show you a nice trick.
you could change custom
var customEventEmitter;
to
this.customEventEmitter =null;
at the top of the function. then you can call
var self = this;
outside of the query function. then inside the query function you reference the outer "this" with self.
as in:
self.customEventEmitter.emit()
the methodology I just described is standard.
When using nodejs event system, I met with an annoying problem. As the code below shows, when a listener catch a event, the event emitter object owns 'this' in the callback function instead of the listener.
It's not a big problem if you put the callback in the listener's constructor, because besides pointer 'this', you can still use other variables defined in the constructor's scope, like the 'self' or 'that' way.
But, if the callback is put outside the constructor like the prototype methods, it appears to me that there is no ways to get the listener's 'this'.
Not very sure if there are any other solutions. And also, a little confused about why nodejs event emit use the emitter as the caller install of the listener?
util = require('util');
EventEmitter = require('events').EventEmitter;
var listener = function () {
var pub = new publisher();
var self = this;
pub.on('ok', function () {
console.log('by listener constructor this: ', this instanceof listener);
// output: by listener constructor this: false
console.log('by listener constructor self: ', self instanceof listener);
// output: by listener constructor this: true
})
pub.on('ok', this.outside);
}
listener.prototype.outside = function () {
console.log('by prototype listener this: ', this instanceof listener);
// output: by prototype listener this: false
// how to access to listener's this here?
}
var publisher = function () {
var self = this;
process.nextTick(function () {
self.emit('ok');
})
}
util.inherits(publisher, EventEmitter);
var l = new listener();
Try binding the listener explicitly to the callback:
pub.on('ok', this.outside.bind(this));
In my module.js I have
var Stream = require('stream');
module.exports = function () {
var stream = new Stream();
stream.readable = true;
stream.emit('data', 'some stuff')
stream.emit('end')
return stream;
}
and in my index.js
var module = require('./module')
module().pipe(process.stdout)
substack's example from the stream handbook is working just fine. Why doesn't my code show anything in the command line?
Because you are emitting data before calling pipe, and 'data' listener is attached after first 'data' event is fired
EventEmitter's calls are synchronous (as almost everything else non-IO in node.js)
A bit simplified version of
stream.emit('data', 'some stuff')
stream.pipe(process.stdout)
without EventEmitter could be rewritten as
stream.listeners = [];
// 'emit' call
var ondata = stream.listeners.data;
if (ondata) {
// only one listener case in the example
ondata('some stuff');
}
// 'pipe' call
stream.listeners.data = function(buff) {
process.write(buff);
}
I am just curious to know what is the purpose of,
function MyStream() {
events.EventEmitter.call(this);
}
in the following block of code taken from Nodejs.org Docs section,
var util = require("util");
var events = require("events");
function MyStream() {
events.EventEmitter.call(this);
}
util.inherits(MyStream, events.EventEmitter);
MyStream.prototype.write = function(data) {
this.emit("data", data);
}
var stream = new MyStream();
console.log(stream instanceof events.EventEmitter); // true
console.log(MyStream.super_ === events.EventEmitter); // true
stream.on("data", function(data) {
console.log('Received data: "' + data + '"');
})
stream.write("It works!"); // Received data: "It works!"
Please explain.
This isn't the first time this question has been asked: https://groups.google.com/forum/#!topic/nodejs/ab_Xih1L5R8/discussion
Quote from Bradley Meck:
Using EventEmitter.call on an object will do the setup of instance methods / properties (not inherited) of an EventEmitter. It is similar in purpose to super(...) in Java or base(...) in C#, but it is not implicit in Javascript. Because of this, we must manually call it ourselves. As for the talk about util.inherits, this will make the MyStream function inherit from another prototyped function so that instanceof works (side note: javascript only allows single inheritance). Due to how the new operator works, if we have the this variable in a function set to an instanceof EventEmitter, and call EventEmitter.call it look for all intents and purposes as if EventEmitter's constructor is being called on our MyStream object.
Here is the error I'm getting when trying to test a basic Socket.io and Express set up (per the example on the socket.io website):
/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:659
var socket = this.namespaces[i].socket(data.id, true);
^
TypeError: Object function extend(another) {
var properties = Object.keys(another);
var object = this;
properties.forEach(function (property) {
object[property] = another[property];
});
return object;
} has no method 'socket'
at Manager.handleClient (/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:659:41)
at Manager.handleUpgrade (/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:588:8)
at HTTPServer.<anonymous> (/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:119:10)
at HTTPServer.emit (events.js:88:20)
at Socket.<anonymous> (http.js:1390:14)
at TCP.onread (net.js:334:27)
Appreciate any help I can get, please :)
This problem stems from the fact that you or a library you use are adding functions to Object.prototype.
Thus this code:
Object.prototype.foo = function() {};
Object.prototype.bar = function() {};
var myObj = { x: 1 };
for (var i in myObj) {
console.log(i)
}
will print: x, foo, bar (not necessarily in that order) and not just x as you expect.
In your case this happens in manager.js:
// initialize the socket for all namespaces
for (var i in this.namespaces) {
var socket = this.namespaces[i].socket(data.id, true);
// echo back connect packet and fire connection event
if (i === '') {
this.namespaces[i].handlePacket(data.id, { type: 'connect' });
}
}
This code doesn't expect to encounter the declared: extend key, as you can see from the stack trace of the error:
TypeError: Object function extend(another) {
var properties = Object.keys(another);
var object = this;
properties.forEach(function (property) {
object[property] = another[property];
});
return object;
} has no method 'socket'
the program is actually tried to invoke socket() on the extend function.
See Bob's rant here about adding functions to Object.prototype.
As for the solution, you can either add a conditional statement inside manager.js like so:
// initialize the socket for all namespaces
for (var i in this.namespaces) {
if ('extend' == i) continue; // ADDED
var socket = this.namespaces[i].socket(data.id, true);
// echo back connect packet and fire connection event
if (i === '') {
this.namespaces[i].handlePacket(data.id, { type: 'connect' });
}
}
or you can remove the Object.prototype.extend = function(...) {} declaration, which is my personal preference.
Your this.namespaces[i].socket(data.id, true); does not exist. Do something like console.log(typeof this.namespaces[i].socket(data.id, true)); You'll probably get an undefined
I bet one of the elements of your namespaces array is missing.