2. About me
Mostovenko Alexander
Python developer at
Writing mostly in python and coffeescript.
Love FP stuff.
Traveling, snowboarding, cycling.
https://twitter.com/MostovenkoA
https://github.com/AlexMost
3. Why Rx.js and not another FRP lib?
● Implemented in a lot of languages
(.NET, Java, Ruby, Python, Clojure, Scala,
Haskell, Objective-C).
● Has a huge amount of docs and examples.
● Why not?
29. Callback vs Black hole
some_async_func(function(err, result) {
// ... looks like we are in trap
another_wonderful_func(function(err, result){
// ... you are in trap
});
});
35. Why we should look at Observable?
1. Promise represents only single value.
2. How to cancel?
3. Lazy execution.
4. We can work with Observable as easy as with
promise.
38. Observable
var source = Rx.Observable.create((observer) =>
// some async operation
observer.onNext(data)
// or ..
observer.onError(data)
// or ..
observer.onCompleted(data)
);
source.subscribe(
(data) => {}
(err) => {}
)
39. Observlable vs promise example
var source = Rx.Observable.create((observer) => {
setTimeout(() => {
console.log("observable timeout hit");
observer.onNext(25);
}, 500);
console.log('observable started')});
source.subscribe(x => console.log(`observable value ${x}`));
Observable
> observable started
> observable timeout hit
> observable value 25
40. Observlable vs promise example
var promise = new Promise((resolve) => {
setTimeout(() => {
console.log("promise timeout hit");
resolve(25);
}, 500);
console.log("promise started");});
promise.then(x => console.log(`promise value ${x}`));
Promise
> promise started
> promise timeout hit
> promise value 25
47. Drag & Drop example
What if i told you ….
That mouse move event is an array and you
can map, filter over it?
demo - http://alexmost.github.io/talks/rx.js/demo/drag_and_drop.html
48. var dragTarget = document.getElementById('dragTarget');
// Get the three major events collections
var mouseup = Rx.DOM.fromEvent(dragTarget, 'mouseup');
var mousemove = Rx.DOM.fromEvent(document, 'mousemove');
var mousedown = Rx.DOM.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(({offsetX, offsetY}) => {
return mousemove
.map(({clientX: x, clientY: y}) => {
return {left: x - offsetX, top: y - offsetY}})
.takeUntil(mouseup)
});
var subscription = mousedrag.subscribe((pos) => {
dragTarget.style.top = pos.top + 'px';
dragTarget.style.left = pos.left + 'px';
});
49. var dragTarget = document.getElementById('dragTarget');
// Get the three major events collections
var mouseup = Rx.DOM.fromEvent(dragTarget, 'mouseup');
var mousemove = Rx.DOM.fromEvent(document, 'mousemove');
var mousedown = Rx.DOM.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(({offsetX, offsetY}) => {
return mousemove
.map(({clientX: x, clientY: y}) => {
return {left: x - offsetX, top: y - offsetY}})
.takeUntil(mouseup)
});
var subscription = mousedrag.subscribe((pos) => {
dragTarget.style.top = pos.top + 'px';
dragTarget.style.left = pos.left + 'px';
});
Create observable collections
from DOM events
50. var dragTarget = document.getElementById('dragTarget');
// Get the three major events collections
var mouseup = Rx.DOM.fromEvent(dragTarget, 'mouseup');
var mousemove = Rx.DOM.fromEvent(document, 'mousemove');
var mousedown = Rx.DOM.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(({offsetX, offsetY}) => {
return mousemove
.map(({clientX: x, clientY: y}) => {
return {left: x - offsetX, top: y - offsetY}})
.takeUntil(mouseup)
});
var subscription = mousedrag.subscribe((pos) => {
dragTarget.style.top = pos.top + 'px';
dragTarget.style.left = pos.left + 'px';
});
Using power of Rx, combine
existing event streams to produce
mouse drag event collection
51. var dragTarget = document.getElementById('dragTarget');
// Get the three major events collections
var mouseup = Rx.DOM.fromEvent(dragTarget, 'mouseup');
var mousemove = Rx.DOM.fromEvent(document, 'mousemove');
var mousedown = Rx.DOM.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(({offsetX, offsetY}) => {
return mousemove
.map(({clientX: x, clientY: y}) => {
return {left: x - offsetX, top: y - offsetY}})
.takeUntil(mouseup)
});
var subscription = mousedrag.subscribe((pos) => {
dragTarget.style.top = pos.top + 'px';
dragTarget.style.left = pos.left + 'px';
});
Subscribe on mouse drag events,
updating top and left attributes
52. Observable are first class objects
Can be:
- passed as a parameter
- returned from a function
- assigned to a variable
56. Challenges
1. Dot or line?
2. Different length of letter codes.
3. Track pause between letters (3 spans).
4. Track pause between words (7 spans).
57. DOM events (keyUp, keyDown, Click, e.t.c)
Signal start, signal end
dots, lines, spaces
letter codes (array of dot’s and lines)
words, letters, sentence
Composable streams
58. DOM events (keyUp, keyDown, Click, e.t.c)
Signal start, signal end
dots, lines, spaces
letter codes (array of dot’s and lines)
words, letters, sentence
Can use each separately
59. Space key down/up as collection
var spaceKeyDowns = Rx.Observable.fromEvent(document, 'keydown').filter((ev) => ev.keyCode == 32)
var spaceKeyUps = Rx.Observable.fromEvent(document, 'keyup').filter((ev) => ev.keyCode == 32)
60. Code for dot’s and lines
var signalStarts = eventStream.filter(({action}) => action === "signal_start").timestamp()
var signalEnds = eventStream.filter(({action}) => action === "signal_end").timestamp()
var signalTimeSpans = signalStarts.flatMap((startArgs) => {
return signalEnds
.map((endArgs) => endArgs.timestamp - startArgs.timestamp)
.first()
})
var dotsStream = signalTimeSpans.filter((v) => v <= SPAN).map(() => ".")
var lineStream = signalTimeSpans.filter((v) => v > SPAN).map(() => "-")
var dotsAndLines = Rx.Observable.merge(dotsStream, lineStream)
61. Code for dot’s and lines
var signalStarts = eventStream.filter(({action}) => action === "signal_start").timestamp()
var signalEnds = eventStream.filter(({action}) => action === "signal_end").timestamp()
var signalTimeSpans = signalStarts.flatMap((startArgs) => {
return signalEnds
.map((endArgs) => endArgs.timestamp - startArgs.timestamp)
.first()
})
var dotsStream = signalTimeSpans.filter((v) => v <= SPAN).map(() => ".")
var lineStream = signalTimeSpans.filter((v) => v > SPAN).map(() => "-")
var dotsAndLines = Rx.Observable.merge(dotsStream, lineStream)
Abstraction on top of key down,
key up, mouse down, mouse up,
e.t.c with timestamp
62. Code for dot’s and lines
var signalStarts = eventStream.filter(({action}) => action === "signal_start").timestamp()
var signalEnds = eventStream.filter(({action}) => action === "signal_end").timestamp()
var signalTimeSpans = signalStarts.flatMap((startArgs) => {
return signalEnds
.map((endArgs) => endArgs.timestamp - startArgs.timestamp)
.first()
})
var dotsStream = signalTimeSpans.filter((v) => v <= SPAN).map(() => ".")
var lineStream = signalTimeSpans.filter((v) => v > SPAN).map(() => "-")
var dotsAndLines = Rx.Observable.merge(dotsStream, lineStream)
Signal duration count
63. Code for dot’s and lines
var signalStarts = eventStream.filter(({action}) => action === "signal_start").timestamp()
var signalEnds = eventStream.filter(({action}) => action === "signal_end").timestamp()
var signalTimeSpans = signalStarts.flatMap((startArgs) => {
return signalEnds
.map((endArgs) => endArgs.timestamp - startArgs.timestamp)
.first()
})
var dotsStream = signalTimeSpans.filter((v) => v <= SPAN).map(() => ".")
var lineStream = signalTimeSpans.filter((v) => v > SPAN).map(() => "-")
var dotsAndLines = Rx.Observable.merge(dotsStream, lineStream)
dot’s stream
lines stream
64. Code for dot’s and lines
var signalStarts = eventStream.filter(({action}) => action === "signal_start").timestamp()
var signalEnds = eventStream.filter(({action}) => action === "signal_end").timestamp()
var signalTimeSpans = signalStarts.flatMap((startArgs) => {
return signalEnds
.map((endArgs) => endArgs.timestamp - startArgs.timestamp)
.first()
})
var dotsStream = signalTimeSpans.filter((v) => v <= SPAN).map(() => ".")
var lineStream = signalTimeSpans.filter((v) => v > SPAN).map(() => "-")
var dotsAndLines = Rx.Observable.merge(dotsStream, lineStream) dot’s and lines !!!
65. Push based collections in result
var lettersStream = letterCodes.map((codes) => morse.decode(codes.join("")))
var wordsStream = lettersStream.buffer(wordsWhitespaces).map((w) => w.join(""))
66. Use Observable as collection
var setCatImgStream = wordsStream.filter((word) => word == "CAT").map(setCatImg)
69. Some common basic operations
map
filter
reduce
zip
merge
flatMap
...
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md