Angular
RxJS
Observables
RxJS

REACTIVE EXTENSIONS LIBRARY FOR JAVASCRIPT
RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code.
Getting started
npm install --save rxjsimport { Observable } from 'rxjs';1. Install via npm
2. Import into your code
Observable
Able to be observed
Iterable...
Observable
Preliminary demo
Rx.Observable
import { Observable } from 'rxjs';
const source = Observable.create(observer => {
let count = 0;
const timer = setInterval(() => {
observer.next(count);
count++;
}, 500);
return () => clearInterval(timer);
});
source.subscribe(
value => { console.log('next: ', value); },
error => { console.log('error: ', error); },
() => { console.log('complete'); }
);
- Three methods: next(), error() and complete()
- Observable is just the function that takes an observer and returns another function
- The returned function is called when an observable is destroyed
- To destroy an observable: subscription.unsubscribe() & observer.complete()
Demo
Observable vs Promise
0 subscriptions
import { Observable } from 'rxjs';
promise = new Promise(resolve => {
console.log('Create Promise');
resolve('promise-result')
});
source = Observable.create(observer => {
console.log('Create Observable');
observer.next('observable-result');
});
1 subscription
import { Observable } from 'rxjs';
promise = new Promise(resolve => {
console.log('Create Promise');
resolve('promise-result')
});
source = Observable.create(observer => {
console.log('Create Observable');
observer.next('observable-result');
});
promise.then(data => console.log('handle:', data));
source.subscribe(value => console.log('handle:', value));
2 subscriptions
import { Observable } from 'rxjs';
promise = new Promise(resolve => {
console.log('Create Promise');
resolve('promise-result')
});
source = Observable.create(observer => {
console.log('Create Observable');
observer.next('observable-result');
});
promise.then(data => console.log('handle:', data));
source.subscribe(value => console.log('handle:', value));
promise.then(data => console.log('handle:', data));
source.subscribe(value => console.log('handle:', value));
Conclusion
- Observable does nothing until it has at least one subscriber.
- Promise runs and created only once. Then it uses a previously obtained result.
- Observable creates producer for each subscription
Creation Operators
.of({})
import { of } from 'rxjs';
const source = of({message: 'any Object'});
const subscription = source.subscribe(
value => console.log('next: ', value),
error => console.error('error: ', error),
() => console.log('complete')
);.from([])
import { from } from 'rxjs';
const source = from([10, 20, 30]);
const subscription = source.subscribe(
value => console.log('next: ', value),
error => console.error('error: ', error),
() => console.log('complete')
);.range(start, count)
import { range } from 'rxjs';
source = range(10, 4);.interval(period)
import { interval } from 'rxjs';
import { take } from 'rxjs/operators';
source = interval(2000)
.pipe(
take(2)
);.throwError()
import { throwError } from 'rxjs';
source = throwError('Damn!');EMPTY
import { EMPTY } from 'rxjs';
import { defaultIfEmpty } from 'rxjs/operators';
source = EMPTY.pipe(
defaultIfEmpty('any default value')
);NEVER
import { NEVER } from 'rxjs';
source = NEVER;Demo
Error handling
.catchError()
import { throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
const source = throwError('Whoops!');
const subscription = source.pipe(
catchError(err => {
console.log('catch:', err);
return of('safety result');
})
)
.subscribe(
value => console.log('next: ', value),
error => console.error('error: ', error),
() => console.log('complete')
);.retry(count)
import { Observable } from 'rxjs';
import { retry } from 'rxjs/operators';
const source = Observable.create(observer => {
console.log('Next attempt');
observer.error('Whoops!');
});
const subscription = source.pipe(
retry(2)
)
.subscribe(
value => console.log('next: ', value),
error => console.error('error: ', error),
() => console.log('complete')
);.retryWhen(err$ => obs)
import { Observable, throwError } from 'rxjs';
import { retryWhen } from 'rxjs/operators';
let flag = true;
const source = Observable.create(observer => {
console.log('Fake server call');
if (flag) {
observer.error('Damn');
flag = false;
} else {
observer.next('success');
}
});
const obs = new Observable(observer => {
console.log('let wait a second');
setTimeout(() => observer.next('any'), 1000);
});
const subscription = source.pipe( retryWhen(err$ => obs) )
.subscribe( value => console.log('next:', value) );.onErrorResumeNext()
import { Observable, throwError } from 'rxjs';
import { retryWhen } from 'rxjs/operators';
source = Observable.create(observer => {
console.log('Attempt');
observer.error('Whoops!');
});
planB = of('Nice solution ;)');
subscription = source.pipe( onErrorResumeNext(planB) )
.subscribe(
value => console.log('next: ', value),
error => console.error('error: ', error),
() => console.log('complete')
);Demo
RxJS operators
RxJS Marbles
Observable.pipe(...)
Observables combination

import { Observable, concat } from 'rxjs';
first = Observable.create(observer => {
setTimeout(() => {
observer.next('500ms');
observer.complete();
}, 500);
});
second = Observable.create(observer => {
setTimeout(() => {
observer.next('200ms');
observer.complete();
}, 200);
});
concat( first, second ).subscribe(val =>
console.log('next:', val)
);
import { Observable, merge } from 'rxjs';
first = Observable.create(observer => {
setTimeout(() => {
observer.next('500ms');
observer.complete();
}, 500);
});
second = Observable.create(observer => {
setTimeout(() => {
observer.next('200ms');
observer.complete();
}, 200);
});
merge( first, second ).subscribe(
val => console.log('next:', val)
);

import { Observable, zip } from 'rxjs';
nextFunction = (label, count, interval) => (observer) => {
let i = 0;
setInterval(() => {
if (i < count) {
observer.next(`[${label}]:${i}`);
i++;
} else {
observer.complete();
}
}, interval);
}
a = Observable.create(nextFunction('A', 3, 500));
b = Observable.create(nextFunction('B', 4, 200));
zip(a, b).subscribe(val => console.log('next:', val));

import { Observable, combineLatest } from 'rxjs';
nextFunction = (label, count, interval) => (observer) => {
let i = 0;
setInterval(() => {
if (i < count) {
observer.next(`[${label}]:${i}`);
i++;
} else {
observer.complete();
}
}, interval);
}
a = Observable.create(nextFunction('A', 3, 500));
b = Observable.create(nextFunction('B', 4, 200));
combineLatest(a, b).subscribe(
val => console.log('next:', val)
);

import { Observable, forkJoin } from 'rxjs';
nextFunction = (label, count, interval) => (observer) => {
let i = 0;
setInterval(() => {
if (i < count) {
observer.next(`[${label}]:${i}`);
i++;
} else {
observer.complete();
}
}, interval);
}
a = Observable.create(nextFunction('A', 3, 500));
b = Observable.create(nextFunction('B', 4, 200));
forkJoin(a, b).subscribe(
val => console.log('next:', val)
);

import { fromEvent } from 'rxjs';
import { switchMap, mapTo } from 'rxjs/operators';
click$ = fromEvent(document, 'click');
click$.pipe(
switchMap(() => {
console.log('call server');
return timer(3000)
.pipe( mapTo('server response') );
})
).subscribe(x => console.log(x));


HOT & COLD
COLD
Create new instance of provider for every subscription
HOT
Create new instance of producer for the first subscription*
* there are few details after complete or error calls
COLD
COLD Observable
Producer 1
Producer 2
Producer N
...
Subscriber 1
Subscriber 2
Subscriber N
...
HOT
Observable
Producer 1
Subscriber 1
Subscriber 2
Subscriber N
Subject
...
Subject
Observable
Observer
+
- .subscribe()
- .pipe()
- .next()
- .error()
- .complete()
Subject
import { Subject } from 'rxjs';
subj = new Subject();
subj.subscribe(
val => console.log('first next:', val),
err => console.log('first was error:', err),
() => console.log('first complete')
);
subj.next('Yes, I can');
subj.complete();
subj.subscribe(
val => console.log('second next:', val),
err => console.log('second was error:', err),
() => console.log('second complete')
);
BehaviorSubject
import { BehaviorSubject } from 'rxjs';
subj = new BehaviorSubject('Default value');
subj.subscribe(
val => console.log('first next:', val),
err => console.log('first was error:', err),
() => console.log('first complete')
);
subj.next( Math.floor((Math.random() * 100)) );
subj.complete();
subj.subscribe(
val => console.log('second next:', val),
err => console.log('second was error:', err),
() => console.log('second complete')
);
ReplaySubject
import { ReplaySubject } from 'rxjs';
subj = new ReplaySubject();
subj.subscribe(
val => console.log('first next:', val),
err => console.log('first was error:', err),
() => console.log('first complete')
);
subj.next( Math.floor((Math.random() * 100)) );
subj.complete();
subj.subscribe(
val => console.log('second next:', val),
err => console.log('second was error:', err),
() => console.log('second complete')
);
AsyncSubject
import { AsyncSubject } from 'rxjs';
subj = new AsyncSubject();
subj.subscribe(
val => console.log('first next:', val),
err => console.log('first was error:', err),
() => console.log('first complete')
);
subj.next( 1 );
subj.next( 2 );
subj.complete();
subj.subscribe(
val => console.log('second next:', val),
err => console.log('second was error:', err),
() => console.log('second complete')
);
Useful links
Q & A
Angular. RxJS. Observables
By Pavel Razuvalau
Angular. RxJS. Observables
- 866