Introduction to Observables with RxJS

Author: Muhammad AbdulMoiz

Who am I?

Author: Muhammad AbdulMoiz

Muhammad AbdulMoiz
Software Developer at Recurship

Technologies i have worked on

Author: Muhammad AbdulMoiz

Javascript
Angular
Ionic
RxJs
ngRx
KoaJs

Discussion topics

  • Reactive programming and Rx 

  • Using observables with RxJS

  • Essential concepts in RxJS

  • Understanding how observal works

  • Understanding observal flow using marble diagrams

  • Discuss a problem when using data stream

Reactive Programming

In computing reactive programming is an asynchronous programming paradigm concerned with data streams and propogation of change

Reactive Programming

Reactive extension is a library for composing asynchronous and event based programs using observables.

RxJS is a reactive extension which performs reactive programming.

Pub/Sub messaging with Observer pattern

Characterstics of Rx libraries

  1. Rx is meant for asynchronous non blocking data processing.
  2.  Rx offers a simple api to send data, error and end of stream signal.
  3. Rx libraries usually have hundred of operators which can perform transformation.
  4. Rx libraries usually have built in support for handling data streaming problems for example handling back pressure ( A scenario when producer is faster then consumer).

 

Observer pattern components

Observal

It is responsible for sending data to subscribers.

Observer

it subscribes to observal to listen for data, error and stream completion

 

Characterstics of Observal

  • Pushing data to subscribers (next method)
  • Throwing errors to subscribers (error method)
  • Notifying subscribers that stream is over (complete method)

Characterstics of Observer

  • Subscribing to observal (subscribe method)
  • Data processing handler
  • Error handler
  • Stream completion handler
  • Unsubsribe to observal (unsubscribe method)

Reviewing flow

Observables maintains a list of observers and not any one can listen to the changes in observables, observers explicitly asks observers to add them to the observer list.

RxJS setup

Installing via npm

npm install rxjs-es

Import and us RxJS

import Rx from 'rxjs/Rx';

Rx.Observable.of(1,2,3)

Import only which you need(size sensitive bundling)


import { Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';

Observable.of(1,2,3).map(x => x + '!!!'); // etc

Observal/Observer using RxJS

Observal setup

var observable = Rx.Observable.create(function (observer) {
  observer.next(1);// Pushing data to subscribers
  observer.next(2);// Pushing data to subscribers
  setTimeout(() => {
        observer.next(3); // Pushing data to subscribers
        observer.complete(); // Notifying end of stream 
    }, 3000);
});

Observer setup

observable.subscribe(
  value => console.log(value), // Received values
  err => {}, // Error thrown
  () => console.log('this is the end') // Marking completion
);

// Logs
// 1
// 2
// 3
// "this is the end"

RxJS Error case

Observal

const observable = Rx.Observable.create((observer) => {
  observer.error('something went really wrong...');
});

Observer

observable.subscribe(
  value => console.log(value), // will never be called
  err => console.log(err),
  () => console.log('complete') // will never be called
);

// Logs
// "something went really wrong..."

RxJS Unsubscribe

Observal

const observable = Rx.Observable.create(observer => {
  const id = setTimeout(() => observer.next('...'), 5000); // emit value after 5s

  return () => { clearTimeout(id); console.log('cleared!'); }; // runs on unsubscribe
});

Observer

const subscription = observable.subscribe(value => console.log(value));

setTimeout(() => subscription.unsubscribe(), 3000); // cancel subscription after 3s

// Logs:
// "cleared!" after 3s

// Never logs "..."

Promises VS Observables

Promises Observables
Emits a single value Emits multiple values over a period of time
Not lazy Lazy i.e not called until subscribed
Can not be cancelled Can be cancelled using unsubscribe method

Essential concepts in RxJS

  • Observables
  • Observer
  • Subscription
  • Operators
  • Subject
  • Schedulers

RxJS Operators

  • Creating operators
  • Transformation operators
  • Filtering operators
  • Multicasting operators
  • Error handling operators
  • Utility operators
  • Conditional and Boolean operator
  • Mathematical and Aggregate operators

Creating operators

From (converts Array to Observable)

var array = [10, 20, 30];
var result = Rx.Observable.from(array);
result.subscribe(x => console.log(x));

// Results in the following:
// 10 20 30

Interval

var numbers = Rx.Observable.interval(1000);
numbers.subscribe(x => console.log(x));
// Emits ascending numbers, one every second (1000ms)

From event

var clicks = Rx.Observable.fromEvent(document, 'click');
clicks.subscribe(x => console.log(x));

// Results in:
// MouseEvent object logged to console everytime a click
// occurs on the document.

Transformation operators

Map

//Map every every click to the clientX position of that click
var clicks = Rx.Observable.fromEvent(document, 'click');
var positions = clicks.map(ev => ev.clientX);
positions.subscribe(x => console.log(x));

Pluck

var clicks = Rx.Observable.fromEvent(document, 'click');
var tagNames = clicks.pluck('target', 'tagName');
tagNames.subscribe(x => console.log(x));

scan

Rx.Observable
  .from([1,2,3,4,5])
	.scan((x,y) => x+y)
// Result stream
//  1, 3, 6, 10, 15

Filtering operators

Filter

//Emit only click events whose target was a DIV element
var clicks = Rx.Observable.fromEvent(document, 'click');
var clicksOnDivs = clicks.filter(ev => ev.target.tagName === 'DIV');
clicksOnDivs.subscribe(x => console.log(x));

throttle

// Emit clicks at a rate of at most one click per second

var clicks = Rx.Observable.fromEvent(document, 'click');
var result = clicks.throttle(ev => Rx.Observable.interval(1000));
result.subscribe(x => console.log(x));

debounceTime

// Add a debounce time of 1000ms
var clicks = Rx.Observable.fromEvent(document, 'click');
var result = clicks.debounceTime(1000);
result.subscribe(x => console.log(x));

Taking a deeper look into Observable.from

function Observable(subscribe) {
  this.subscribe = subscribe;
}

Observable.from = (dataSet) => {
  return new Observable((observer) => {
    dataSet.forEach((value) => observer.next(value));
    return () => dataSet = [];
  });
};

const input$ = Observable.from([1,2,3,4,5,6]);

const unsubscribe = input$.subscribe({
  next: (value) => {
    console.log( value);
  }
});

// automatically unsubscribe after 5s
setTimeout(unsubscribe, 5000);

Taking a deeper look into Observable.fromEvent

const node = document.querySelector('input');
const p = document.querySelector('p');

function Observable(subscribe) {
  this.subscribe = subscribe;
}

Observable.fromEvent = (element, name) => {
  return new Observable((observer) => {
    const callback = (event) => observer.next(event);
    element.addEventListener(name, callback, false);
    return () => element.removeEventListener(name, callback, false);
  });
};

const input$ = Observable.fromEvent(node, 'input');

const unsubscribe = input$.subscribe({
  next: (event) => {
    p.innerHTML = event.target.value;
  }
});

// automatically unsub after 5s
setTimeout(unsubscribe, 5000);

RxJS Subject

RxJs subjects are at a time observable as well as observer

var subject = new Rx.Subject();

subject.subscribe({
  next: (v) => console.log('observerA: ' + v),
  complete: () => console.log('completed observerA')
});
subject.subscribe({
  next: (v) => console.log('observerB: ' + v),
  complete: () => console.log('completed observerB')
});

subject.next(1);
subject.next(2);
subject.complete();
// Output
observerA: 1
observerB: 1
observerA: 2
observerB: 2
completed observerA
completed observerB

RxJS BehaviourSubject

It stores the latest value used and on new subscriptions that value is passed to subscribers for the first time

var subject = new Rx.BehaviorSubject(0); // 0 is the initial value

subject.subscribe({
  next: (v) => console.log('observerA: ' + v)
});

subject.next(1);
subject.next(2);

subject.subscribe({
  next: (v) => console.log('observerB: ' + v)
});

subject.next(3);

// Output

observerA: 0
observerA: 1
observerA: 2
observerB: 2
observerA: 3
observerB: 3

RxJS ReplaySubject

A replay can save multiple values and emit them at the time of subscription

var subject = new Rx.ReplaySubject(3); // buffer 3 values for new subscribers

subject.subscribe({
  next: (v) => console.log('observerA: ' + v)
});

subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);

subject.subscribe({
  next: (v) => console.log('observerB: ' + v)
});

subject.next(5);

// Output

observerA: 1
observerA: 2
observerA: 3
observerA: 4
observerB: 2
observerB: 3
observerB: 4
observerA: 5
observerB: 5

RxJS AsyncSubject

It only sends last value before completion to all of its subscribers

var subject = new Rx.AsyncSubject();

subject.subscribe({
  next: (v) => console.log('observerA: ' + v)
});

subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);

subject.subscribe({
  next: (v) => console.log('observerB: ' + v)
});

subject.next(5);
subject.complete();

// Output
observerA: 5
observerB: 5

RxJS Marble Diagrams

Explain data stream flow

Example Marble Diagram

Zip

Example Marble Diagram

Merge

Example Marble Diagram

Merge

Back pressure in data streaming

Back pressure is a scenario when producer is faster than the consumer, We may have data losses if this is not handled correctly. We need to properly handle the case in which consumer can't keep up with the producer's pace.

 

Handling back pressure in RXJS

Pausable buffers

var source = getStockData()
  .pausableBuffered();

source.subscribeOnNext(function (stock) {
  console.log('Stock data: %o', stock);
});

source.pause();

// Resume after five seconds
setTimeout(function () {
  // Drains the buffer and subscribeOnNext is called with the data
  source.resume();
}, 5000);

Handling back pressure in RXJS

Controlled observers

var source = getStockData()
  .controlled();

source.subscribeOnNext(function (stock) {
  console.log('Stock data: %o', stock);
});

source.request(2);

// Keep getting more after 5 seconds
setInterval(function () {
  source.request(2);
}, 5000);

RxJS

A word of caution. Rx libraries allow you to write less code, but the code readability suffer. The person who reads the code needs to know Rx as well.

Observables and RxJS

Questions please ????

Made with Slides.com