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,768