42
Streams FunctionalJs - London - May 2014 @darachennis

FunctionalJS - May 2014 - Streams

  • Upload
    darach

  • View
    381

  • Download
    0

Embed Size (px)

DESCRIPTION

Talk about Node.js streams and related

Citation preview

Page 1: FunctionalJS - May 2014 - Streams

StreamsFunctionalJs - London - May 2014

@darachennis

Page 2: FunctionalJS - May 2014 - Streams
Page 3: FunctionalJS - May 2014 - Streams

A <3able Stream// by @TooTallNate - https://gist.github.com/TooTallNate/3947591 // npm install lame ; npm install speaker ; node mp3player.js

var fs = require('fs');

var lame = require('lame');

var Speaker = require('speaker');

fs.createReadStream(process.argv[2])

.pipe(new lame.Decoder())

.on('format', function (format) {

this.pipe(new Speaker(format));

});

Page 4: FunctionalJS - May 2014 - Streams

A <3able Stream// by @TooTallNate - https://gist.github.com/TooTallNate/3947591 // npm install lame ; npm install speaker ; node mp3player.js

var fs = require('fs');

var lame = require('lame');

var Speaker = require('speaker');

fs.createReadStream(process.argv[2])

.pipe(new lame.Decoder())

.on('format', function (format) {

this.pipe(new Speaker(format));

});

Page 5: FunctionalJS - May 2014 - Streams

Sound Soupvar fs = require('fs');

var lame = require('lame');

var Speaker = require('speaker');

!// Cache names of mp3's in cwd

function isMp3(e) { return e.indexOf("mp3") > 1 };

var soundz = fs.readdirSync('.').filter(isMp3);

!// Every half second, play a random sound

setInterval(function(){

blend() },500);

function rand(min, max) {

var offset = min;

var range = (max - min) + 1;

return Math.floor( Math.random() * range) + offset;

}

function blend() {

fs.createReadStream(soundz[rand(0,soundz.length-1)])

.pipe(new lame.Decoder())

.on('format', function (format) {

this.pipe(new Speaker(format));

});

}

Page 6: FunctionalJS - May 2014 - Streams

A brief history in Node.js streams

Page 7: FunctionalJS - May 2014 - Streams

UNIXy• UNIX $ a | b | c | d

• NODE.js> a.pipe(b).pipe(c).pipe(d)

• same as:

• a.pipe(b);b.pipe(c);c.pipe(d);// but, less typing for the win \o/

Page 8: FunctionalJS - May 2014 - Streams

Streams 0

• Util.pump(Readable,Writable)

Page 9: FunctionalJS - May 2014 - Streams

Streams 1

• EventEmitter and a pipe() function.

• pipe() is an instance of the Builder Pattern

• EventEmitter is an instance of Subject/Observer Pattern

Page 10: FunctionalJS - May 2014 - Streams

Streams 1: Examplevar Stream = require(‘stream').Stream

var util = require('util')

function MyStream () {

Stream.call(this)

}

util.inherits(MyStream, Stream)

// stream logic and state management and back-pressure

// you’re on your own (with the rest of the pipeline)

// this was error prone and painful …

Page 11: FunctionalJS - May 2014 - Streams

Streams 2

• EventEmitter and a pipe() function.

• Readable, Writable, Duplex, Transform, Passthrough (and classic) ‘behaviours'

• New ‘behaviours’ encourage good ‘backpressure' handling disciplines

Page 12: FunctionalJS - May 2014 - Streams

Streams 2: Using Readable// From: Stream Handbook, Readable with Push

var Readable = require('stream').Readable;

var rs = new Readable;

rs.push(‘beep ‘);

rs.push(‘boop\n’);

rs.push(null);

rs.pipe(process.stdout);

Page 13: FunctionalJS - May 2014 - Streams

Streams 2: Using Readable// From: Stream Handbook

var Readable = require('stream').Readable;

var rs = Readable();

var c = 97;

rs._read = function () {

rs.push(String.fromCharCode(c++));

if (c > 'z'.charCodeAt(0)) rs.push(null);

};

rs.pipe(process.stdout); // _read called once, prints ‘a-z'

Page 14: FunctionalJS - May 2014 - Streams

Streams 2: Pop Quiz!!!$ node readable-popquiz.js

abcdefghijklmnopqrstuvwxyz% $

!

Page 15: FunctionalJS - May 2014 - Streams

Streams 2: Pop Quiz!!!// From: Stream Handbook (tweaked)

var Readable = require('stream').Readable;

var rs = Readable();

var c = 97;

rs._read = function () {

rs.push(String.fromCharCode(c++));

if (c > 'z'.charCodeAt(0)) rs.push(null);

};

rs.pipe(process.stdout); // _read called once

rs.pipe(process.stdout); // _read called twice

rs.pipe(process.stdout); // _read called three times

rs.pipe(process.stdout); // _read called four times

Page 16: FunctionalJS - May 2014 - Streams

Streams 2: Pop Quiz!!!$ node readable-popquiz.js

aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmmnnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzz% $

… WAT? (Think about it)

!

Page 17: FunctionalJS - May 2014 - Streams

Streams 2: Using Writable// From: Stream Handbook

var fs = require('fs');

var ws = fs.createWriteStream('message.txt');

ws.write('beep ‘); // Write …

setTimeout(function () {

ws.end(‘boop\n'); // We’re done writing

}, 1000);

Page 18: FunctionalJS - May 2014 - Streams

Streams 2: Using Writablevar Writable = require('stream').Writable;

var src = process.stdin;

var dst = new Writable;

dst._write = function (chunk, enc, next) {

console.log(chunk.toString());

next();

};

src.push("hit any key!");

src.pipe(dst);

Page 19: FunctionalJS - May 2014 - Streams

Streams 2: Transformvar Transform = stream.Transform || require('readable-stream').Transform;

function Hash(algorithm, encoding, options) {

Transform.call(this, options);

this.digester = crypto.createHash(algorithm);

this.encoding = encoding;

}

util.inherits(Hash, Transform);

Hash.prototype._transform = function (chunk, enc, cb) {

var bf = (Buffer.isBuffer(chunk)) ? chunk : new Buffer(chunk, enc);

this.digester.update(bf);

cb();

};

Hash.prototype._flush = function (cb) {

this.push(this.digester.digest(this.encoding));

cb();

};

Page 20: FunctionalJS - May 2014 - Streams

Streams 2: Transformvar hash = require('./hash.js');

var fs = require('fs');

var rs = fs.createReadStream(‘file.js’);

var md5Hash = hash.Hash('md5', 'base64');

// var sha1Hash = hash.Hash('sha1', 'base64');

rs.pipe(md5Hash).pipe(process.stdout);

Page 21: FunctionalJS - May 2014 - Streams

Streams 2: Duplex* Independent Readable and Writable channels

* Implement _read and _write

* Example use - network protocols - serial communications with hardware - …

Page 22: FunctionalJS - May 2014 - Streams

Know a classic stream// From: Stream Handbook

process.stdin.on('data', function (buf) {

console.log(buf);

});

process.stdin.on('end', function () {

console.log('__END__');

});

Page 23: FunctionalJS - May 2014 - Streams

Streams 3

• Streams2 tweaked

• API the same as Streams2

Page 24: FunctionalJS - May 2014 - Streams

Useful modules

• @DominicTarr’s event-stream https://github.com/dominictarr/event-stream

• @RVagg’s through2https://github.com/rvagg/through2

• @Raynos’s duplexerhttps://github.com/Raynos/duplexer

Page 25: FunctionalJS - May 2014 - Streams

• https://github.com/substack/stream-handbook - @SubStack’s Stream handbook

• http://www.nearform.com/nodecrunch/dont-use-nodes-core-stream-module#.U4I71JRdWuk - Why @Rvagg doesn’t use node’s core stream module (& nor should we!)

Good to know …

Page 26: FunctionalJS - May 2014 - Streams

Meta Programming with Streams and Pipes

Great British Node Conference October 8th 2013

London !

Darach Ennis

Page 27: FunctionalJS - May 2014 - Streams

Streams 101

! Readable Streams ! IO / throughput oriented ! Events – data, end, error, close ! Methods – pause, resume, end, destroy

! Writeable Streams ! IO / throughput oriented ! Events - drain, pause, resume, error, close ! Methods – write, end, destroy

Page 28: FunctionalJS - May 2014 - Streams

Streams … 4IO

! IO Oriented

! Finite, not infinite

! Asynchronous

! Lazy

! Assumes in transit data, not in memory forms

! Great for IO. Not efficient for compute

Page 29: FunctionalJS - May 2014 - Streams

CSV Reader .@maxogden

Page 30: FunctionalJS - May 2014 - Streams

Beams

Streams for Compute

Page 31: FunctionalJS - May 2014 - Streams

Beams 101

! Sources ! Where data pours in

! Sinks ! Where results pop out

! Operators ! Filters – Drop or pass on data based on a UDF ! Transforms – Mutate data based on a UDF ! Branch with Filters ! Combine with Sinks

Page 32: FunctionalJS - May 2014 - Streams

Beams… 4CPU

! Compute Oriented

! Infinite, not finite

! Synchronous

! Eager

! Assumes in memory form, not encoded or wire data

! Convenient for compute. Not good for IO

Page 33: FunctionalJS - May 2014 - Streams

Beams - Branch

Page 34: FunctionalJS - May 2014 - Streams

Beams - Combine

Page 35: FunctionalJS - May 2014 - Streams

Ex – Fly NodeCopter with Streams!

Page 36: FunctionalJS - May 2014 - Streams

Extend Games to Robots!

Page 37: FunctionalJS - May 2014 - Streams

Extend?

Meta Programming

Page 38: FunctionalJS - May 2014 - Streams

A minor problem

! Eager: a.pipe(operation).pipe(z).pipe(a);

! Implies:RangeError: Maximum call stack size exceeded

Page 39: FunctionalJS - May 2014 - Streams

Goto + Modules = Magic

Page 40: FunctionalJS - May 2014 - Streams

Extend Beam with Modules

Page 41: FunctionalJS - May 2014 - Streams

Questions & Thanks

! npm install beam ! Compute oriented streams ! Branch, Combine, Filter, Transform data ! Extend with goto and modules.

! npm install eep ! Beam will be integrated into eep soon ! Eep provides aggregate windows ! Slide, Tumble, Monotonic, Periodic ! Stats Library ! Ordering now supported

! npm install nodesapiens ! Robosapiens V1 for node.js

! Code on github ! https://github.com/darach

Page 42: FunctionalJS - May 2014 - Streams

Dojo - nodeschool.io

• Basics

• Stream Adventure

• Functional Javascript