Skip to content

Instantly share code, notes, and snippets.

@zcaceres
Created March 24, 2017 15:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zcaceres/afe3a9a816178a9677f87fd5f8e0c260 to your computer and use it in GitHub Desktop.
Save zcaceres/afe3a9a816178a9677f87fd5f8e0c260 to your computer and use it in GitHub Desktop.
Events and Socket.io

Events and Socket.io

FSA 1702, March 24, 2017


Event Emitters

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.

Making an Example Event Emitter

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.

Practical Uses

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

Everywhere in Node

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 Part Two

HTTP: It transfers hypertext through cyberspace.

Think of a live feed of news coverage. We want live coverage to immediately display to the user.

Long-Polling

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.

TCP and other Stuff

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

Websockets and Socket.io

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment