SlideShare a Scribd company logo
ASYNC PIPELINES
in client-side JavaScript
(an experiment)
ismael celis . new-bamboo.co.uk . github.com/ismasan
V/Capp.AppView = Backbone.View.extend({	
	
initialize: function () {	
...	
},	
	
render: function () {	
...	
},	
!
addOne: function (todo) {	
...	
},	
	
addAll: function () {	
...	
},	
	
filterOne: function (todo) {	
todo.trigger('visible');	
},	
	
filterAll: function () {	
...	
}	
!
}	
Mapp.Todo = Backbone.Model.extend({	
!
defaults: {	
title: '',	
completed: false	
},	
	
toggle: function () {	
this.save({	
completed: !this.get('completed')	
});	
},	
	
processAndSave: function (data, callback) {	
this.set(data)	
var self = this	
this.save(function () {	
callback()	
self.trigger('processed')	
Todo.trigger('processed', self)	
})	
}	
});	
T
<h1>{{ count }} productos encontrados</h1>	
!
<ul>	
{{#products}}	
<li><a href="{{ href }}">{{name}}</a></li>	
{{/products}}	
</ul>
+ verbs
- nouns
How my JS apps should be built
How my JS apps end up being built
...
BASIC OBJECT
var Person = BasicObject.extend({

initialize: function (name) {

this.name = name

},

talk: function () {

alert("What's up!")

}

})



var Programmer = Person.extend({

talk: function () {

alert("Go away, I'm busy writing code!")

}

})
STRUCT
var mario = new Struct({name: 'Mario'})	
!
mario.set('name', 'Luigi') // triggers `change:name` event	
!
mario.get('name') // “Luigi"	
!
mario.uid() // "uid13983601141350.9170819637365639"
var p1 = new Pipe()	
var p2 = new Pipe()	
var p3 = new Pipe()	
!
p1.pipe(p2).pipe(p3)	
!
p3.on('add', function (struct) {	
console.log('p3 received data!', struct)	
})	
!
p1.add(mario) 	
// forwards mario to other pipes downstream, 	
// including p2 and then p3	
PIPE
PIPE
pipe(anotherPipe)	

!
add(struct)	

!
remove(struct)
PIPE
!
add(struct)	

!
filter(struct, filterPromise)	

!
_add(struct, addPromise)	

!
_formwardAdd(struct)
PIPE + FILTER
var MarioFilter = Pipe.extend({	
!
filter: function (struct, filterPromise) {	
if(struct.get('name') == 'Mario') filterPromise.resolve(struct)	
else filterPromise.reject(struct)	
}	
!
})
PIPE + FILTER
var filter = new MarioFilter()	
!
filter.pipe(p2).pipe(p3)	
!
filter.add(new Struct({name: 'Mario'})) 	
// p2 and p3 get struct added	
!
filter.add(new Struct({name: 'Luigi'})) 	
// filter is rejected. p2 and p3 DO NOT get struct added
PIPE + FILTER + AJAX
var MarioFilter = Pipe.extend({	
	
filter: function (struct, filterPromise) {	
$.get(	
'http://some.api.com/valid_mario', 	
{name: struct.get('name')}	
).then(	
function () { filterPromise.resolve(struct) }, // success	
function () { filterPromise.reject(struct) } // error	
)	
}	
	
})
PIPE
api.jquery.com/jQuery.when/
$.when(	
	
pipe1.add(struct), 	
pipe2.add(struct), 	
pipe3.add(struct)	
	
).then(...)
INDEX
var Index = Pipe.extend({



initialize: function () {

this._index = {}
this._list = []

},
!
…

})
INDEX
var Index = Pipe.extend({



…



_add: function (struct, addPromise) {



if(! this._index[struct.uid()]) {

this._index[struct.uid()] = struct
this._list.push(struct)

addPromise.resolve(struct)

}



}

})
INDEX
var index = new Index()	
index.add(mario)	
index.add(luigi)	
!
index.pipe(another_pipe) 	
// ‘another_pipe’ will get mario and luigi added to it now	
!
index.add(princess) 	
// ‘another_pipe’ gets princess added to it.
INDEX
index.add(mario) // forwards to other pipes	
!
mario.set(age: 30)	
!
index.add(mario) // does not forward.
CAPPED INDEX
var CappedIndex = Index.extend({	
!
limit: 10,	
!
_add: function (struct, promise) {	
// remove first if limit reached	
if(this._list.length > this.limit - 1) 	
		 	 this.remove(this._list[0])	
// add next	
return Index.prototype._add.call(this, struct, promise)	
}	
!
})
DEVICES
CHOKE POINT
var p1 		 	 	 = new Pipe()	
var p2 		 	 	 = new SomeAjaxPipe()	
var results = new Pipe()	
var choke 		 = new ChokePoint(p1, p2)	
!
choke.pipe(results)	
!
choke.add(struct)
PIPELINE
var p1 		 	 	 	 = new Pipe()	
var p2 		 	 	 	 = new SomeAjaxPipe()	
var results 	 = new Pipe()	
var pipeline 	= new LeakyPipeline(p1, p2)	
!
pipeline.add(struct) 	
// will forward struct to p1, p2 and finally pipe on to results pipe
FAN IN
var in1 		 	 	 	 	 = new Pipe() 	
var in2 		 	 	 	 	 = new Pipe()	
var results 		 	 = new Pipe()	
var fanIn		 	 	 	 = new Ventilator([in1, in2])	
!
fanIn.pipe(results)	
!
in1.add(struct) // forwards struct to results
FAN OUT
var out1 		 	 	 	 	 = new Pipe() 	
var out2 		 	 	 	 	 = new Pipe()	
!
var fanOut		 	 	 = new Ventilator(null, [out1, out2])	
!
fanOut.add(struct) // forwards struct to out1 and out2
MANY-TO-MANY
var ventilator = new Ventilator(	
[in1, in2], 	
[out1, out2]	
)	
!
in1.add(struct) // forwards struct to out1 and out2	
in2.add(struct) // forwards struct to out1 and out2
ROUTER
var p1 	 	 	 = new Pipe()	
var p2 	 	 	 = new Pipe()	
var default	= new Pipe()	
!
var router 	= new Router()	
!
router	
.route(p1, function (struct, promise) {	
if(struct.get('name') == 'Joe') promise.resolve()	
else promise.reject()	
})	
.route(p2, function (struct, promise) {	
if(struct.get('name') == 'Jane') promise.resolve()	
else promise.reject()	
})	
.default(default)
ROUTER
router.add(new Struct({name: 'Joe'})) 	
// forwards struct to p1	
!
router.add(new Struct({name: 'Jane'})) 	
// forwards struct to p2	
!
router.add(new Struct({name: 'Paul'})) 	
// forwards struct to default	
!
router.pipe(other) // pipes all data to `other`.
CUSTOM DEVICE
var MyCustomDevice = Pipe.extend({	
!
filter: function (struct, filterPromise) {...},	
!
_add: function (struct, addPromise) {...},	
!
_remove: function (struct, removePromise) {...},	
!
_forwardAdd: function (struct) {...},	
!
_forwardRemove: function (struct) {...}	
})
CUSTOM DEVICE
Throttle, Buffer,Aggregator, Counter, Identity Map, State Machine
REPOSITORY
// users.pipe(results).get('http://some.api.com/users')	
var Users = Pipe.extend({	
!
// Get from remote API	
get: function (url) {	
var self = this	
!
$.getJSON(url).then(function (data) {	
data.forEach(function (user) {	
self.add(user) 	
})	
})	
return this	
}	
})
REPOSITORY
var Users = Pipe.extend({	
!
// A repository can have its own index	
initialize: function () {	
this.__index = new Index(UserStruct) 	
// pipe index to itself so repository can pipe to other pipes transparently	
this.__index.pipe(this)	
},	
!
// Get from remote API	
get: function (url) {	
var self = this	
!
$.getJSON(url).then(function (data) {	
data.forEach(function (user) {	
self.__index.add(user) 	
})	
})	
return this	
}	
})
REPOSITORY
var Users = Pipe.extend({	
!
_add: function (struct, addPromise) {	
// send data to server	
$.ajax(this.url, {	
type: 'post',	
dataType: 'json',	
data: struct.attributes	
}).then(function (data) {	
struct.set(data) 	
// update struct with data coming from server	
addPromise.resolve(struct)	
})	
}	
!
})
FRAMEWORK ?
VIEW
var View = Pipe.extend({	
	
initialize: function ($e) {	
...	
	
this._children = {}	
},	
	
_add: function (item, promise) {	
// create and index child view	
this._children[item.uid()] = new ChildElement(this.$itemTemplate)	
	
promise.resolve(item)	
},	
	
_remove: function (item, promise) {	
// unbind, destroy and de-index child-view	
var child = this._children[item.uid()].destroy()	
delete this._children[item.uid()]	
	
promise.resolve(item)	
}	
})
VIEW
rivetsjs.com
<div id="items">	
	
<ul data-item>	
<li data-text="item.name"></li>	
</ul>	
	
</div>
VIEW
var view = new View($('#items'))	
!
var mario = new Struct({name: 'Mario'})	
!
view.add(mario)
CAPPED VIEW
function cappedView($e, limit) {	
var index = new CappedIndex(limit)	
var view = new View($e)	
index.pipe(view)	
return index	
}
APP
var timeline = cappedView($('#timeline'), 10)	
!
var socket = new SocketRepository('ws://some.server.com')	
!
socket.pipe(timeline)	
gist.github.com/ismasan/5848735
github.com/ismasan/plumber.js
reactive-extensions.github.io/RxJS/
ismael celis . new-bamboo.co.uk . github.com/ismasan

More Related Content

ODP
Scala 2 + 2 > 4
PDF
The Ring programming language version 1.10 book - Part 94 of 212
PDF
Rich and Snappy Apps (No Scaling Required)
PDF
Finding Clojure
PDF
The Ring programming language version 1.9 book - Part 91 of 210
PDF
Extreme JavaScript Performance
PDF
Say It With Javascript
PDF
The Ring programming language version 1.6 book - Part 15 of 189
Scala 2 + 2 > 4
The Ring programming language version 1.10 book - Part 94 of 212
Rich and Snappy Apps (No Scaling Required)
Finding Clojure
The Ring programming language version 1.9 book - Part 91 of 210
Extreme JavaScript Performance
Say It With Javascript
The Ring programming language version 1.6 book - Part 15 of 189

What's hot (20)

PDF
The Ring programming language version 1.7 book - Part 16 of 196
PDF
GPars For Beginners
PDF
Javascript ES6 generators
PPT
Come on, PHP 5.4!
PDF
Rxjs vienna
PDF
The Ring programming language version 1.5.2 book - Part 9 of 181
PPTX
Using Redux-Saga for Handling Side Effects
KEY
連邦の白いヤツ 「Objective-C」
TXT
Interfaz Grafica En Java
PDF
The Ring programming language version 1.5.1 book - Part 33 of 180
PDF
Universal JavaScript
PDF
Building Real Time Systems on MongoDB Using the Oplog at Stripe
PDF
Building Real Time Systems on MongoDB Using the Oplog at Stripe
PDF
RxJS - 封裝程式的藝術
PDF
누구나 할 수 있다 Networking
PDF
The Ring programming language version 1.9 book - Part 15 of 210
PPTX
The redux saga begins
PDF
The Ring programming language version 1.5.3 book - Part 88 of 184
PPTX
PDF
ES6 generators
The Ring programming language version 1.7 book - Part 16 of 196
GPars For Beginners
Javascript ES6 generators
Come on, PHP 5.4!
Rxjs vienna
The Ring programming language version 1.5.2 book - Part 9 of 181
Using Redux-Saga for Handling Side Effects
連邦の白いヤツ 「Objective-C」
Interfaz Grafica En Java
The Ring programming language version 1.5.1 book - Part 33 of 180
Universal JavaScript
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
RxJS - 封裝程式的藝術
누구나 할 수 있다 Networking
The Ring programming language version 1.9 book - Part 15 of 210
The redux saga begins
The Ring programming language version 1.5.3 book - Part 88 of 184
ES6 generators
Ad

Similar to Async data pipelines for client-side JavaScript (20)

PDF
Chaining and function composition with lodash / underscore
PDF
Writing RESTful web services using Node.js
PPTX
Developing web-apps like it's 2013
PDF
Building Smart Async Functions For Mobile
PDF
Streams API (Web Engines Hackfest 2015)
PPT
JS everywhere 2011
PPT
RESTful API In Node Js using Express
PPT
Server side JavaScript: going all the way
PDF
"#Microfrontends #LowConnectivity #AsianMarket", Maxim Demidenko
KEY
Practical Use of MongoDB for Node.js
PPT
Building your first Node app with Connect & Express
PDF
An approach to responsive, realtime with Backbone.js and WebSockets
PDF
Building Killer RESTful APIs with NodeJs
PDF
MEAN Stack WeNode Barcelona Workshop
KEY
How and why i roll my own node.js framework
PDF
Intro to node.js - Ran Mizrahi (27/8/2014)
PDF
Intro to node.js - Ran Mizrahi (28/8/14)
PDF
Intro to Sail.js
PPTX
Sencha Touch - Introduction
PPTX
Promise it's partial
Chaining and function composition with lodash / underscore
Writing RESTful web services using Node.js
Developing web-apps like it's 2013
Building Smart Async Functions For Mobile
Streams API (Web Engines Hackfest 2015)
JS everywhere 2011
RESTful API In Node Js using Express
Server side JavaScript: going all the way
"#Microfrontends #LowConnectivity #AsianMarket", Maxim Demidenko
Practical Use of MongoDB for Node.js
Building your first Node app with Connect & Express
An approach to responsive, realtime with Backbone.js and WebSockets
Building Killer RESTful APIs with NodeJs
MEAN Stack WeNode Barcelona Workshop
How and why i roll my own node.js framework
Intro to node.js - Ran Mizrahi (27/8/2014)
Intro to node.js - Ran Mizrahi (28/8/14)
Intro to Sail.js
Sencha Touch - Introduction
Promise it's partial
Ad

Recently uploaded (20)

PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Product Update: Alluxio AI 3.7 Now with Sub-Millisecond Latency
PDF
Autodesk AutoCAD Crack Free Download 2025
PDF
AutoCAD Professional Crack 2025 With License Key
PDF
How to Make Money in the Metaverse_ Top Strategies for Beginners.pdf
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PPTX
L1 - Introduction to python Backend.pptx
PPTX
Monitoring Stack: Grafana, Loki & Promtail
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PPTX
Operating system designcfffgfgggggggvggggggggg
DOCX
Greta — No-Code AI for Building Full-Stack Web & Mobile Apps
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
Salesforce Agentforce AI Implementation.pdf
PDF
CCleaner Pro 6.38.11537 Crack Final Latest Version 2025
PPTX
Advanced SystemCare Ultimate Crack + Portable (2025)
How to Choose the Right IT Partner for Your Business in Malaysia
Product Update: Alluxio AI 3.7 Now with Sub-Millisecond Latency
Autodesk AutoCAD Crack Free Download 2025
AutoCAD Professional Crack 2025 With License Key
How to Make Money in the Metaverse_ Top Strategies for Beginners.pdf
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
L1 - Introduction to python Backend.pptx
Monitoring Stack: Grafana, Loki & Promtail
CHAPTER 2 - PM Management and IT Context
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Design an Analysis of Algorithms II-SECS-1021-03
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Operating system designcfffgfgggggggvggggggggg
Greta — No-Code AI for Building Full-Stack Web & Mobile Apps
Odoo Companies in India – Driving Business Transformation.pdf
Navsoft: AI-Powered Business Solutions & Custom Software Development
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Salesforce Agentforce AI Implementation.pdf
CCleaner Pro 6.38.11537 Crack Final Latest Version 2025
Advanced SystemCare Ultimate Crack + Portable (2025)

Async data pipelines for client-side JavaScript

  • 1. ASYNC PIPELINES in client-side JavaScript (an experiment) ismael celis . new-bamboo.co.uk . github.com/ismasan
  • 2. V/Capp.AppView = Backbone.View.extend({ initialize: function () { ... }, render: function () { ... }, ! addOne: function (todo) { ... }, addAll: function () { ... }, filterOne: function (todo) { todo.trigger('visible'); }, filterAll: function () { ... } ! } Mapp.Todo = Backbone.Model.extend({ ! defaults: { title: '', completed: false }, toggle: function () { this.save({ completed: !this.get('completed') }); }, processAndSave: function (data, callback) { this.set(data) var self = this this.save(function () { callback() self.trigger('processed') Todo.trigger('processed', self) }) } }); T <h1>{{ count }} productos encontrados</h1> ! <ul> {{#products}} <li><a href="{{ href }}">{{name}}</a></li> {{/products}} </ul>
  • 4. How my JS apps should be built
  • 5. How my JS apps end up being built
  • 6. ...
  • 7. BASIC OBJECT var Person = BasicObject.extend({
 initialize: function (name) {
 this.name = name
 },
 talk: function () {
 alert("What's up!")
 }
 })
 
 var Programmer = Person.extend({
 talk: function () {
 alert("Go away, I'm busy writing code!")
 }
 })
  • 8. STRUCT var mario = new Struct({name: 'Mario'}) ! mario.set('name', 'Luigi') // triggers `change:name` event ! mario.get('name') // “Luigi" ! mario.uid() // "uid13983601141350.9170819637365639"
  • 9. var p1 = new Pipe() var p2 = new Pipe() var p3 = new Pipe() ! p1.pipe(p2).pipe(p3) ! p3.on('add', function (struct) { console.log('p3 received data!', struct) }) ! p1.add(mario) // forwards mario to other pipes downstream, // including p2 and then p3 PIPE
  • 12. PIPE + FILTER var MarioFilter = Pipe.extend({ ! filter: function (struct, filterPromise) { if(struct.get('name') == 'Mario') filterPromise.resolve(struct) else filterPromise.reject(struct) } ! })
  • 13. PIPE + FILTER var filter = new MarioFilter() ! filter.pipe(p2).pipe(p3) ! filter.add(new Struct({name: 'Mario'})) // p2 and p3 get struct added ! filter.add(new Struct({name: 'Luigi'})) // filter is rejected. p2 and p3 DO NOT get struct added
  • 14. PIPE + FILTER + AJAX var MarioFilter = Pipe.extend({ filter: function (struct, filterPromise) { $.get( 'http://some.api.com/valid_mario', {name: struct.get('name')} ).then( function () { filterPromise.resolve(struct) }, // success function () { filterPromise.reject(struct) } // error ) } })
  • 16. INDEX var Index = Pipe.extend({
 
 initialize: function () {
 this._index = {} this._list = []
 }, ! …
 })
  • 17. INDEX var Index = Pipe.extend({
 
 …
 
 _add: function (struct, addPromise) {
 
 if(! this._index[struct.uid()]) {
 this._index[struct.uid()] = struct this._list.push(struct)
 addPromise.resolve(struct)
 }
 
 }
 })
  • 18. INDEX var index = new Index() index.add(mario) index.add(luigi) ! index.pipe(another_pipe) // ‘another_pipe’ will get mario and luigi added to it now ! index.add(princess) // ‘another_pipe’ gets princess added to it.
  • 19. INDEX index.add(mario) // forwards to other pipes ! mario.set(age: 30) ! index.add(mario) // does not forward.
  • 20. CAPPED INDEX var CappedIndex = Index.extend({ ! limit: 10, ! _add: function (struct, promise) { // remove first if limit reached if(this._list.length > this.limit - 1) this.remove(this._list[0]) // add next return Index.prototype._add.call(this, struct, promise) } ! })
  • 22. CHOKE POINT var p1 = new Pipe() var p2 = new SomeAjaxPipe() var results = new Pipe() var choke = new ChokePoint(p1, p2) ! choke.pipe(results) ! choke.add(struct)
  • 23. PIPELINE var p1 = new Pipe() var p2 = new SomeAjaxPipe() var results = new Pipe() var pipeline = new LeakyPipeline(p1, p2) ! pipeline.add(struct) // will forward struct to p1, p2 and finally pipe on to results pipe
  • 24. FAN IN var in1 = new Pipe() var in2 = new Pipe() var results = new Pipe() var fanIn = new Ventilator([in1, in2]) ! fanIn.pipe(results) ! in1.add(struct) // forwards struct to results
  • 25. FAN OUT var out1 = new Pipe() var out2 = new Pipe() ! var fanOut = new Ventilator(null, [out1, out2]) ! fanOut.add(struct) // forwards struct to out1 and out2
  • 26. MANY-TO-MANY var ventilator = new Ventilator( [in1, in2], [out1, out2] ) ! in1.add(struct) // forwards struct to out1 and out2 in2.add(struct) // forwards struct to out1 and out2
  • 27. ROUTER var p1 = new Pipe() var p2 = new Pipe() var default = new Pipe() ! var router = new Router() ! router .route(p1, function (struct, promise) { if(struct.get('name') == 'Joe') promise.resolve() else promise.reject() }) .route(p2, function (struct, promise) { if(struct.get('name') == 'Jane') promise.resolve() else promise.reject() }) .default(default)
  • 28. ROUTER router.add(new Struct({name: 'Joe'})) // forwards struct to p1 ! router.add(new Struct({name: 'Jane'})) // forwards struct to p2 ! router.add(new Struct({name: 'Paul'})) // forwards struct to default ! router.pipe(other) // pipes all data to `other`.
  • 29. CUSTOM DEVICE var MyCustomDevice = Pipe.extend({ ! filter: function (struct, filterPromise) {...}, ! _add: function (struct, addPromise) {...}, ! _remove: function (struct, removePromise) {...}, ! _forwardAdd: function (struct) {...}, ! _forwardRemove: function (struct) {...} })
  • 30. CUSTOM DEVICE Throttle, Buffer,Aggregator, Counter, Identity Map, State Machine
  • 31. REPOSITORY // users.pipe(results).get('http://some.api.com/users') var Users = Pipe.extend({ ! // Get from remote API get: function (url) { var self = this ! $.getJSON(url).then(function (data) { data.forEach(function (user) { self.add(user) }) }) return this } })
  • 32. REPOSITORY var Users = Pipe.extend({ ! // A repository can have its own index initialize: function () { this.__index = new Index(UserStruct) // pipe index to itself so repository can pipe to other pipes transparently this.__index.pipe(this) }, ! // Get from remote API get: function (url) { var self = this ! $.getJSON(url).then(function (data) { data.forEach(function (user) { self.__index.add(user) }) }) return this } })
  • 33. REPOSITORY var Users = Pipe.extend({ ! _add: function (struct, addPromise) { // send data to server $.ajax(this.url, { type: 'post', dataType: 'json', data: struct.attributes }).then(function (data) { struct.set(data) // update struct with data coming from server addPromise.resolve(struct) }) } ! })
  • 35. VIEW var View = Pipe.extend({ initialize: function ($e) { ... this._children = {} }, _add: function (item, promise) { // create and index child view this._children[item.uid()] = new ChildElement(this.$itemTemplate) promise.resolve(item) }, _remove: function (item, promise) { // unbind, destroy and de-index child-view var child = this._children[item.uid()].destroy() delete this._children[item.uid()] promise.resolve(item) } })
  • 36. VIEW rivetsjs.com <div id="items"> <ul data-item> <li data-text="item.name"></li> </ul> </div>
  • 37. VIEW var view = new View($('#items')) ! var mario = new Struct({name: 'Mario'}) ! view.add(mario)
  • 38. CAPPED VIEW function cappedView($e, limit) { var index = new CappedIndex(limit) var view = new View($e) index.pipe(view) return index }
  • 39. APP var timeline = cappedView($('#timeline'), 10) ! var socket = new SocketRepository('ws://some.server.com') ! socket.pipe(timeline) gist.github.com/ismasan/5848735