Author: Muhammad AbdulMoiz
Author: Muhammad AbdulMoiz
Muhammad AbdulMoiz
Software Developer at Recurship
Author: Muhammad AbdulMoiz
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
In computing reactive programming is an asynchronous programming paradigm concerned with data streams and propogation of change
Reactive extension is a library for composing asynchronous and event based programs using observables.
RxJS is a reactive extension which performs reactive programming.
Observal
It is responsible for sending data to subscribers.
Observer
it subscribes to observal to listen for data, error and stream completion
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.
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 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"
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..."
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 | 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 |
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.
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
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));
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);
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 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
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
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
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
Explain data stream flow
Zip
Merge
Merge
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.
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);
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);
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.