Avraam Mavridis
AvraamMavridis
avraamakis
Web Developer
Async Javacript using Reactive Extensions
How we are doing async programming in JS right now?
- Callbacks
- Promises
- Event Emitters
Callback's Problems (1)
Callback hell
app.post('/process-file', function(req, res) {
var inputFile = 'input.txt';
var outputFile = 'output.txt';
fs.readFile(inputFile, function(err, data) {
if (err) return res.status(500).send(err);
process1(data, function(err, data) {
if (err) return res.status(500).send(err);
process2(data, function(err, data) {
if (err) return res.status(500).send(err);
process3(data, function(err, data) {
if (err) return res.status(500).send(err);
fs.writeFile(outputFile, data, function(err) {
if (err) return res.status(500).send(err);
res.status(200).send('processed successfully using callback hell');
});
});
});
});
});
Callback's Problems (2)
Tricky to catch errors with proper error semantics
const boo = msg => setTimeout( msg => { throw Error( msg )}, 1000 )
const fun = ( msg, callback )=> {
console.log( msg );
callback( msg );
}
try{
fun('You will never catch me', boo )
}
catch( e )
{
console.log( e )
}
Callback's Problems (3)
Imagine the scenario where you pass a callback to a third-party library, you can't control how many times the library will be invoked
Callbacks can run more than once.
Promise's Problems (1)
That makes them useless on handling recurrent events e.g. mouse clicks, websocket streams etc.
Promises yield only a single value
Promise's Problems (2)
Imagine the scenario where there is a button in the UI and every time the button is pressed we send a request to the server, there is no guarantee on the order of the server's responses
Promises are not cancelable
Event Emitter's Problems(1)
Event listener functions usually ignore their return value, which forces the listener to have side effects if it wants to mutate the state of the app.
They force side effects
Event Emitter's Problems(2)
It is easy to miss events if we start listening to late. There are cases where the event emitter fires before the listener subscribes, losing the event forever.
Easy to miss events
Event Emitter's Problems(3)
Usually we're limited to handle each event individually and not as a sequence and only after the events happens.
Limited handling
What is Reactive Programming?
"Reactive Programming is a programming paradigm oriented around data flows and the propagation of change." -Wikipedia
"Reactive Programming integrates time flow and compositional events into functional programming." -Haskell Docs
"Reactive programming supports elegant programming of dynamic and reactive systems by providing first-class, composable abstractions for behaviors and events" -Elliott and Hudak 1997
"Reactive programming is programming with data streams."
+ Raise the level of abstraction
+ Seperate business logic / implementation details
+ Minimize side effects
A little bit of history
Lisp, The first FP language. -John McCarthy,1958
"Can Programming Be Liberated From the von Neumann Style?" -John Bakus,Tunning Award 1977
Miranda, lazy, purely functional programming language that doesn't side effects and imperative programming features. -1985
The first release of Haskel -1990
"Functional Reactive Animation" -The first paper on reactive programming, 1997
Elm. -Evan Czaplicki, 2012
Reactive Extensions 1.0 release by Microsoft -2010
RxJava -Netflix, 2013
FRP Libraries in JS
Highland
Kefir
Bacon
RxJS
Examples
Write a function that will log the first 5 keypresses of the "ALT" button
let count = 0;
document.addEventListener( 'keydown', function logmouse( e ){
if( e.which === 18 && count < 5 )
{
count++;
console.log( "ALT button have been pressed" );
}
else
{
document.removeEventListener( 'keydown', logmouse );
}
});
Example 1
Write a function that will log the first 5 keypresses of the "ALT" button
let count = 0;
document.addEventListener( 'keydown', function logmouse( e ){
if( e.which === 18 && count < 5 )
{
count++;
console.log(`ALT button have been pressed ${count} times`);
}
else
{
document.removeEventListener( 'keydown', logmouse );
}
});
Global state
Side effects
Unrelated code
Of course we can improve our code and eliminate these problems, but aren't we already introduce too much complexity for such a simple scenario?
Example 1
Example 1
Rx.Observable.fromEvent( document, 'keydown' )
.filter( e => e.which === 18 )
.take( 5 )
.subscribe( e => console.log( "Alt event has been pressed" );
RxJS Version
- Easier to read
- No need to create external variables to keep state
- No need to clean up, so no chance to introduce memory leak by forgetting to unregister listeners
Example 2
let counter = 0;
let resUI = document.getElementById('result');
document.getElementById('+').addEventListener( 'click', () => { counter++; updateUI(); } );
document.getElementById('-').addEventListener( 'click', () => { counter--; updateUI(); } );
const updateUI = () => {
resUI.innerText = counter;
}
updateUI();
Implement a "two-button" counter
Example 2
let counter = 0;
let resUI = document.getElementById('result');
document.getElementById('+').addEventListener( 'click', () => { counter++; updateUI(); } );
document.getElementById('-').addEventListener( 'click', () => { counter--; updateUI(); } );
const updateUI = () => {
resUI.innerText = counter;
}
updateUI();
Side effects
Global State
We call updateUI in more than one place.
- The flow of the program is not immediately noticeable
- The flow is deeply nested with the implementation
Example 2
const rxresult = document.getElementById('rx-result');
const plus = Rx.Observable.fromEvent( document.getElementById('rx+'), 'click' )
.map( () => +1 );
const minus = Rx.Observable.fromEvent( document.getElementById('rx-'), 'click' )
.map( () => -1 );
const counter = Rx.Observable.merge( plus, minus )
.startWith( 0 )
.scan( ( sum, next ) => sum + next )
.subscribe( result => rxresult.innerText = result );
We raised the level of abstraction making the flow more clear
FRP is all about describing a system in terms of time-varying functions instead of mutable state
The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration.
The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration.
Imperative Programming
Functional Reactive Programming
counter := 0
on buttonUp = (counter := counter + 1)
on buttonDown = (counter := counter - 1)
counter = accumulate ($) 0
(fmap (+1) eventUp
union fmap (-1) eventDown)
Example 3
const button = document.getElementById("myBtn");
const getData = function(){
return fetch( 'https://api.github.com/users/AvraamMavridis' )
.then( response => response.json() );
}
button.addEventListener( 'click', e => {
getData().then( response => {
alert( response );
} );
});
We have a button, when the user presses the button we send an AJAX request to the server and we alert the response
What happens if the user keeps pressing the button before the response returns from the server?
Example 3
const button = document.getElementById("myBtn");
const getData = function(){
return fetch( 'https://api.github.com/users/AvraamMavridis' )
.then( response => response.json() );
}
button.addEventListener( 'click', e => {
button.disabled = true;
getData().then( response => {
alert( response );
button.disabled = false;
} )
});
We can disable the button when the user sends the request and enable it again...
What if the server takes 10sec to response, will we have the button disabled for 10sec? Not the greatest UX experience...
Example 3
const getData2 = function(){
return Rx.Observable.fromPromise(
fetch('https://api.github.com/users/AvraamMavridis')
.then( response => response.json() )
);
}
Rx.Observable.fromEvent(document.getElementById("myBtn2"), 'click' )
.debounce( 1000 )
.flatMapLatest(getData2)
.subscribe( response => console.log( response ) )
Implementation using Rx
Lets Create a Store for React Components using ES7 decorators
Lets Create an Autocomplete Component with Rx
More to check:
Thank you
AvraamMavridis
avraamakis
avraam.mavridis
Async Javascript using Reactive Extensions
By Avraam Mavridis
Async Javascript using Reactive Extensions
- 1,797