Deep Dive into RxJS

~ Pavithra ~

@PKodmad

 I <3 Javascript

 UI Engineer @ Flipkart.com

Javascript

  • Single call stack
  • Async handling of events
  • Async handling of data sources

Callbacks

  • ​Very much needed
  • Context changes
  • Bad readability
  • Limited reuse
  • Increased complexity when unstructured
var isLoaded = false;

$.ajax({
    url:'http://blah.api.com',
    success:function(resp){
        isLoaded = true;
    }
});

var isLoaded = false;
var isBlah1Loaded = false;
var isBlah2Loaded = false;
$.ajax({
    url:'blah1',
    success:function(resp){
        isBlah1Loaded = true;
        if(isBlah2Loaded){
            isLoaded = true;
        }
    }
});

$.ajax({
    url:'blah2',
    success:function(resp){
        isBlah2Loaded = true;
        if(isBlah1Loaded){
            isLoaded = true;
        }
    }
});

But there are promises!

Promise cons

  • Processes when invoked.
  • Not disposable
  • What about event listener callbacks?

Something better?

Lets look at RxJS

 

A concept of Reactive extensions.

Java, c#, Python, Ruby, Scala

and Javascript!

Rx.js - A primer

  • Reactive extensions

Reactive Observables

 

Collection of values over time is modeled as an Observable in Rx.js

 

 

Reactive Observables

 

Can model

  • Events
  • Data Requests
  • Animations

 

Stream => Infinite length/lazily evaluated.

 

observable

observer

do stuff
onCompleted
onError
onNext

observer

side effects
side effects

var searchWikipedia = function(query){
  var promise = $.ajax({
    url:"https://en.wikipedia.org/w/api.php",
    dataType:'jsonp',
    data:{
      action:"query",
      format:"json",
      list:"search",
      srsearch:query
    }
  }).promise();
  
  return Rx.Observable.fromPromise(promise);
}

Rx.Observable.from(['Led Zeppelin', 'The Kinks', 'CCR']);
Rx.Observable.create(function(observer){
    try{
        if(condition){
            observer.onNext(42);
        } else {
            observer.onCompleted();
        }
    }catch(e){
        observer.onError(e);
    }

    return disposeFn;
});

Rx.js - A primer

  1. A concept of reactive extensions.
  2. Api for Observable

Subscriber(observer)

var source = Rx.Observable.from(['Led Zeppelin', 'The Kinks', 'CCR']);

source.subscribe(function(value){
    console.log(value)
}, function(error){
    console.log('error')
}, function(){
    console.log('done');
});

//OUTPUT

//Led Zeppelin
//The Kinks
//CCR
//done

Rx.js - A primer

  1. A concept of reactive extensions.
  2. Api for Observable
  3. Subscribe to an Observable

map

filter

flatMap

concatMap

sample

skip

takeUntil

take

delay

distinctUntilChanged

find

join

max

mergeAll

reduce

pluck

retry

throttle

debounce

transduce

zip

and many more...

Combinators!

Rx.js - A primer

  1. A concept of reactive extensions.
  2. Api for Observable
  3. Subscribe to an Observable
  4. Combinators
var queries = ['Led Zeppelin', 'Pink Floyd', 'The Kinks', 'Foo Fighters', 'Doobie Brothers'];

var queryObs = Rx.Observable.from(queries);

var source = queryObs
.flatMap(searchWikipedia)
.map(function(searchRes){ return searchRes.query.search[2].title; })
.zip(queries, function(s1, s2){
                return s2 + ' - [Wiki response]' + s1;
            });

var sub = source.subscribe(function(val){
  console.log(val);
}, function(err){
  console.log(err);
}, function(done){
  console.log('done');
});

Xhr with Rx Observables

Observables

Reactive

Combinators

Functional

Map

Delay

Debounce

Autocomplete widget

var $input = $('#textInput');

    // Get all distinct key up events from the input and only fire if long enough and distinct
var source = Rx.Observable.fromEvent($input, 'keyup')
              .map(function (e) {
                  return e.target.value; // Project the text from the input
              })
              .filter(function (text) {
                return text.length > 2; // Only if the text is longer than 2 characters
              })
              .debounce(750 /* Pause for 750ms */ )
              .distinctUntilChanged() // Only if the value has changed
              .flatMapLatest(searchWikipedia)

source.subscribe(function (data) {
        updateResults(data);
      },
      function (error) {
        showError();
      });

Drag and Drop with Rxjs

var dragTarget = $('#dragTarget'), 
    mouseup = dragTarget.onAsObservable('mouseup'), 
    mousemove = dragTarget.onAsObservable('mousemove'), 
    mousedown = dragTarget.onAsObservable('mousedown')
                .map(function (event) {
		    event.preventDefault();
		    return { 
                      left: event.clientX - dragTarget.offset().left, 
                      top: event.clientY - dragTarget.offset().top 
                    };
                }), 
    mousedrag = mousedown.flatMapLatest(function(imageOffset) {
		    return mousemove.select(function (pos) {
		        return {
		            left: pos.clientX - imageOffset.left, 
                            top: pos.clientY - imageOffset.top
                        };
		    }).takeUntil(mouseup);
	        });
    mousedrag.subscribe (function (pos) {
	$('#dragTarget').css({top: pos.top, left: pos.left});
    });
  • Include rx.*.js
  • rx.jquery.js
  • Rx.Observable.create
  • Rx.Observable.from
  • Rx.Observable.fromEvent
  • Rx.Observable.fromCallback 
  • Rx.Observable.fromNodeCallback
  • Rx.Observable.fromPromise

Vanilla Js/Jquery/Node

For your MV* Framework

  • Hot and cold Observables
  • Backpressure
  • Structuring Rx code

Advanced stuff

  • Declarative Programming
  • Localising side effects
  • Error Handling
  • Composability
  • Streams FTW!
  • Robust
    • Tried and tested
    • Browser compatible
    • Performant

Takeaways

Alternatives

FRP in production

Resources

~finis~

 

@PKodmad

Made with Slides.com