Using WebSockets

In notebook:
FrontEndMasters Modular Web Development
Created at:
2017-10-01
Updated:
2017-10-01
Tags:
JavaScript Node JS Fundamentals

Adding a server, that has a websockets connection.

We will add a visitor counter on the bottom of the page.

  var wsock = require('websocket-stream')
var http = require('http')
// **** 10. handle when someone disconnects ↴
var onend = require('end-of-stream')
// **** 2.   ↴
var ecstatic = require('ecstatic') // for static assets
var st = ecstatic(__dirname + '/public') // serve from public
var router = require('routes')()

router.addRoute('GET /user/:name', function (req, res, m) {
  res.end('name=' + m.params.name + '\n')
})

// **** 1. set up our server  ↴
var server = http.createServer(function (req, res) {
  var m = router.match(req.method + ' ' + req.url)
  if (m) m.fn(req,res,m)
  // **** 3.  ↴
  else st(req,res) // static will handle this
})
server.listen(5000)

// **** 5. number of times people have visited  ↴
var count = 0
// **** 7. keep track of all the connected clients  ↴
var streams = []
// **** 4. set up the websocket server  ↴
wsock.createServer({ server: server }, function (stream) {
  // we get a duplex stream
  // but for now we will only push data to the clients
  // **** 8. register a new client  ↴
  streams.push(stream)
  console.log('CONNECTED', streams.length)
  count++
  // **** 9. notify ALL of the connected clients  ↴
  streams.forEach(function (s) {
    // **** 6. send the counter  ↴
    s.write(count + '\n')
  })
  // **** 11. when someone disconnects ↴
  onend(stream, function () {
    var ix = streams.indexOf(stream)
    // **** 12. uses splice to remove the disconnected client ↴
    streams.splice(ix,1)
    console.log('DISCONNECTED', streams.length)
  })
})

The updates the package.json file :

  //	****		package.json		****

{
  "scrips": {
    // **** 1. adds the dev and build scripts  ↴
    "dev": "wacthify yo.js -o public/bundle.js -dv",
    "build": "browserify yo.js | uglifyjs | gzip > public/bundle.js.gz"
  },
  "dependencies": {
    ..
  }
}

$ npm run dev ☛ Now every time we edit yo.js or any of the files it depends on, it will be rebundled.

Add the html file

  <body><script> src="bundle.js"</script></body>

This is enough for now.

Later we can generate the hmtl on the server.

Listen for websocket events in the browser

  //	****		yo.js		****

var html = require('yo-yo')
// **** 1. add the websocket lib  ↴
const wsock = require('websocket-stream')
// **** 2. get the handle for the stream  ↴
var stream = wsock('ws://localhost:5000') // or location.host
// **** 3. split in newlines  ↴
var split = require('split2')
// **** 4. consume the stream  ↴
var to = require('to2')
// **** 5. use the stream  ↴
stream.pipe(split()).pipe(to(function (row, enc, next) {
  bus.emit('increment-visitors', Number(buf.toString())
  next()
}))
var state = {n : 5, visitors :  0}

const EventEmitter = require('events')
var bus = new EventEmitter 
require('./reduce.js')(bus, state)

bus.on('update', update)

bus.on('increment-x', ..)

var root = document.body.appendChild(document.createElement('div'))
update()

setInterval(function () {
  bus.emit('increment-n')
}, 1000)


function update () {
  html.update(root, html`<div> 
    <h1>${state.n}</h1>
    add the coaddunter display
    <div>${state.x}</div>
    <button onclick=${onclick}>CLICK ME</button>
  </div>`)
  function onclick (ev) {
    emit('increment-x')
  }
}

Make sure to update also reduce.js to use visitors instead of x...

The complete file from GitHub:

  var html = require('yo-yo')
var wsock = require('websocket-stream')
var split = require('split2')
var to = require('to2')
var stream = wsock('ws://' + location.host)
stream.pipe(split()).pipe(to(function (buf, enc, next) {
  bus.emit('set-visitors', Number(buf.toString()))
  bus.emit('update')
  next()
}))

var state = {
  visitors: 0,
  x: 0
}
var EventEmitter = require('events')
var bus = new EventEmitter
require('./reduce.js')(bus, state)

var root = document.body.appendChild(document.createElement('div'))
update()
bus.on('update', update)

function update () {
  html.update(root, html`<div>
    <h1>${state.visitors}</h1>
    <div>${state.x}</div>
    <button onclick=${onclick}>CLICK ME</button>
  </div>`)
  function onclick (ev) {
    bus.emit('increment-x')
  }
}

We just need to update the reduce.js so that it does not increment but sets the correct number of visitors:

  //	****		reduce.js		****

module.exports = function (bus, state) {
  // **** 1. use set visitors   ↴
  bus.on('set-visitors', function (n) {
    state.visitors = n
    bus.emit('update')
  })
  bus.on('increment-x', function () {
    state.x = (state.x + 1) % 4
    bus.emit('update')
  })
}

Question: How would you do it with TDD?

Start with the parts that are relatively easy to test. The reducer for example. This is easy to test, no DOM specific parts.

The other parts would need some refactoring to be able to test. Best is to split out the rendering to another file.