FSA 1702, March 24, 2017
Objects that can emit specific events with a payload to any number of registered listeners.
Event Emitters look a bit like event handlers. We're familiar with listening for events, but emitters take this one step further.
Event Emitters come with two functions. .on
and .emit
.
// Listening for an event
userTweets.on('newTweet', function(tweet) {
console.log(tweet);
});
// Somewhere else in the program...
// Emit an event! Give it a name and a payload (data)
userTweets.emit('newTweet', {
text: 'Check out this fruit I ate!'
});
These follow the design pattern: Pub/Sub Pattern or Observer/Observable Pattern
Event emitters and listeners are part and parcel of an event-driven environment like Node.
var event = require('events'); // built in node module for events
var EventEmitter = events.EventEmitter; // constructor function in events
var myEmitterInstance = new EventEmitter();
// Listener Template
// myEmitterInstance.on(eventName, function() {
//
// });
// Emitter Template
// myEmitterInstance.emit(eventName, payload);
myEmitterInstance.on('currentTime', function(event) {
console.log(timeNow);
});
myEmitterInstance.on('currentTime', function(event) {
console.log('Pizza is ready');
});
// ORDER MATTERS. Your listener must be there before the event is emitted, or else no one is listening to your event.
myEmitterInstance.emit('currentTime', {
timeNow: new Date();
});
Event emitters are cool because you can emit and event and have listeners throughout your program that 'hear' them.
// currentTimeListener.js
// We can modularize our Event Emitters like this:
var events = require('events');
var EventEmitter = events.EventEmitter;
var myEmitterInstance = new EventEmitter();
var myEventListener = myEmitterInstance.on('currentTime', function(event) {
console.log(timeNow);
});
module.exports = {
myEmitterInstance: myEmitterInstance,
myEventListener: myEventListener
}
// currentTimeEmitter.js
// We can modularize our Event Emitters like this:
var myEmitterInstance = require('./currentTimeListener.js').myEmitterInstance
// ORDER MATTERS. Your listener must be there before the event is emitted, or else no one is listening to your event.
var myEventEmitter = myEmitterInstance.emit('currentTime', {
timeNow: new Date();
});
module.exports = myEventEmitter;
// back in our index.js ...
/* We just import our listener and emitter in the right order (listener first) to run them */
require('./currentTimeListener.js');
require('./currentTimeEmitter.js');
Now our listeners and emitters are modularized.
It connects two decoupled parts of an application.
Event Emitters are similar to promises in that they solve the problem of 'timing' events correctly. Events are asynchronous, except they are constantly listening.
Think Spotify.
var currentTrack = new EventEmitter();
currentTrack.emit('changeTrack', newTrack);
currentTrack.on('changeTrack', function (newTrack) {
// display new Track!
});
Allows us to invoke change throughout the app, depending on what a user does.
Represent multiple asynchronous events on a single entity
An event emitter can do sustained async work. WE can have plenty of events on a single
You'll see these all over the place:
server.on('request')
request.on('data') / request.on('end')
process.stdin.on('data');
db.on('connection')
Streams
var EventEmitter = function() {
this.subscribers = {
}
}
// Inside this.subscribers -- we have keys with our events and our callbacks as their values`
// 'pizza': [listenerOneFunction, listenerTwoFunction],
// 'currentDate': [functionOne, functionTwo, functionThree],
// 'partyTime': [myFn]
EventEmitter.prototype.on = function(eventName, listenerFunction) {
if(!this.subscribers[eventName]) {
this.subscribers[eventName] = [];
}
this.subscribers[eventName].push(listenerFn);
}
EventEmitter.prototype.emit = function(eventName, payload) {
if (!this.subscribers[eventName]) {
return;
}
this.subscribers[eventName].forEach(function(fn) {
fn(payload);
});
}
var MyEmitter = new EventEmitter();
myEmitter.on('graduation', function() {
console.log(name + ' is graduating');
})
myEmitter.emit('graduation', 'Omri');
HTTP: It transfers hypertext through cyberspace.
Think of a live feed of news coverage. We want live coverage to immediately display to the user.
We can refresh content by periodically refreshing the page – wastes a lot of data and sort of sucks.
Servers can also receive one request and hold onto it. After a while, the server will respond with anything new -- if anything new happened.
- client makes a request
- server polls DB regularly
- when server finally has an update, it send data to the client
- client responds with 'here's the last thing I got'
- server is always aware of 'the last thing the client got'
- if there's a difference, the server sends it something new
Problem: the client must always solicit data from the server because HTTP is a request/response protocol.
Server can't push data to the client.
How does the internet work? Read this paper so you don't flunk your job interview.
The internet is a protocol stack. At the bottom is IP, internet protocol. IT deals with how things are addressed. When things are connected to the internet, it gets an ID. This is the bottom.
IP – How we address TCP – How we deliver to these addresses
Transport Layer Next to that is TCP, transmission control protocol. This is how we DELIVER the addresses. Establishes a reliable connection. Lasts until one side ends it. Either side can send bits.
- Port
- Connections
- Retries/control flow
Application Layer Protocol On top of this is HTTP. HTTP uses TCP/IP. It is a standardized way to communicate between IPs. Usually uses 80.
- Uses a three-way handshake to create a session between server and client.
- Request/Response
- Server timeout or client closes connection
We don't have to use HTTP. We have a TCP connection open already!
`use strict`
// Built in moduel for raw TCP connections.
var net = require('net');
var fs = require('fs');
var telnetServer = net.createServer();
telnetServer.listen(8124);
telnetServer.on('connection', function(connection) {
//do stuff
connection.write('welcome!');
});
// Send a text file after a second
setInterval(function() {
fs.readFile('./text-one.txt'), function(err, data) {
connection.write(data.toString());
}, 1000)
})
SocketIO is a duet of libraries for client and server.
Get your SocketIO running with a script tag on your front end. Require it in the back. You have a connection... whoa.
A raw TCP connection between client and server.
- Starts with HTTP GET request to change the protocol.. saying: upgrade to websocket!
- Server says, ok, our connection is being upgraded.
- A raw TCP connection websocket is now available.
Happily, we do not need to set up our websocket itself. Socket.io does this for us. It relies heavily on event emitters.
Socket.IO needs access to your Express server. So return the app.listen()
to a variable so you can use it later.
// BACK END
...
var io = socketio(server);
io.on('connection', function(socket) {
console.log(socket.id)
});
io.on('mousemove', function(x,y) {
// When user moves their mouse on the screen. It goes to the server and runs this listener. This broadcasts the mousemove event to everyone with another event called othermousemove.
// broadcasts to every other client
io.sockets.emit('othermousemove', x, y);
});
io.on('othermousemove', function(x,y) {
var $div = $('<div></div>');
// create a div with CSS here
// append div to body at position x y
setTimeOut(function() {
$div.remove()
}, 400)
})
})
...
// FRONT END
...
var socket = io(window.location.origin)
socket.on('connect', function() {
console.log('connected successfully');
})
$('body').on('mousemove', function(event) {
var x = event.clientX;
var y = event.clientY;
socket.emit('mousemove', x, y);
})
...
Data flows from client to socket to server and back again, based on event listening and emission.