"Once you stop learning, you start dying"
- Albert Einstein
RACHEL HEIMBACH
Developer
rheimbach@quintor.nl
@Rach_Nerd
Reactive Extensions
single items | multiple items | |
---|---|---|
synchronous | T getData() | Iterable<T> getData() |
asynchronous | Future<T> getData() | Observable<T> getData() |
http://reactivex.io/intro.html
extends ObserverPattern
event | Iterable (pull) | Observable (push) |
---|---|---|
retrieve data | T next() | onNext(T) |
discover error | throws Exception | onError(Exception) |
complete | !hasNext() | onCompleted() |
Streams
Data
Observable
N amount of operators
New Data/Event
Event
N amount of subscribers
Time
Http
Computations
Observable
import io.reactivex.Observable;
Observable
.just("utrecht")
.subscribe(System.out::println);
just
// ------ Output ------
// utrecht
import io.reactivex.Observable;
Observable
.fromArray(
"groningen",
"enschede",
"utrecht",
"amersfoort"
)
.subscribe(System.out::println);
fromArray
// ------ Output ------
// groningen
// enschede
// utrecht
// amersfoort
import io.reactivex.Observable;
Observable
.fromArray(
"groningen",
"enschede",
"utrecht",
"amersfoort"
)
.map(StringUtils::capitalize)
.subscribe(System.out::println);
map
// ------ Output ------
// Groningen
// Enschede
// Utrecht
// Amersfoort
import io.reactivex.Observable;
Observable
.fromArray(
"groningen",
"enschede",
"utrecht",
"amersfoort"
)
.map(StringUtils::capitalize)
.zipWith(Observable.range(1, Integer.MAX_VALUE),
(city, n) ->
String.format("%2d. %s", n, city)
)
.subscribe(System.out::println);
zipWith
// ------ Output ------
// 1. Groningen
// 2. Enschede
// 3. Utrecht
// 4. Amersfoort
import io.reactivex.Observable;
Observable
.fromArray(
"groningen",
"enschede",
"utrecht",
"amersfoort"
)
.switchMap(city -> Observable.fromArray(city.split("")))
.map(StringUtils::capitalize)
.zipWith(Observable.range(1, Integer.MAX_VALUE),
(city, n) ->
String.format("%2d. %s", n, city)
)
.subscribe(System.out::println);
switchMap
// ------ Output ------
// 1. G
// 2. R
// 3. O
// 4. N
// 5. I
// 6. N
// 7. G
// 8. E
// 9. N
// 10. E
// 11. N
// 12. S
// 13. C
// 14. H
// 15. E
// 16. D
// 17. E
// 18. U
// 19. T
// 20. R
// 21. E
// 22. C
...
import io.reactivex.Observable;
Observable
.fromArray(
"groningen",
"enschede",
"utrecht",
"amersfoort"
)
.switchMap(city -> Observable.fromArray(city.split("")))
.map(StringUtils::capitalize)
.sorted()
.zipWith(Observable.range(1, Integer.MAX_VALUE),
(city, n) ->
String.format("%2d. %s", n, city)
)
.subscribe(System.out::println);
sorted
// ------ Output ------
// 1. A
// 2. C
// 3. C
// 4. D
// 5. E
// 6. E
// 7. E
// 8. E
// 9. E
// 10. E
// 11. F
// 12. G
// 13. G
// 14. H
// 15. H
// 16. I
// 17. M
// 18. N
// 19. N
// 20. N
// 21. N
...
import io.reactivex.Observable;
Observable
.fromArray(
"groningen",
"enschede",
"utrecht",
"amersfoort"
)
.switchMap(city -> Observable.fromArray(city.split("")))
.distinct()
.map(StringUtils::capitalize)
.sorted()
.zipWith(Observable.range(1, Integer.MAX_VALUE),
(city, n) ->
String.format("%2d. %s", n, city)
)
.subscribe(System.out::println);
distinct
// ------ Output ------
// 1. A
// 2. C
// 3. D
// 4. E
// 5. F
// 6. G
// 7. H
// 8. I
// 9. M
// 10. N
// 11. O
// 12. R
// 13. S
// 14. T
// 15. U
import io.reactivex.Observable;
Observable
.just("groningen")
.flatMap(city -> Observable.fromArray(city.split("")))
.distinct()
.map(StringUtils::capitalize)
.sorted()
.zipWith(Observable.range(1, Integer.MAX_VALUE),
(city, n) ->
String.format("%2d. %s", n, city)
)
.subscribe(System.out::println);
Recap
groningen
g r o n i n g e n
g r o n i e
E G I N O R
G R O N I E
123456...
3. I 4. N 5. O 6. R
E G I N O R
2. G
1. E
Subject
"A Subject is a special type of Observable that allows values to be multicasted to many Observers" - Reactivex
PublishSubject
PublishSubject
import io.reactivex.subject.Subject;
import io.reactivex.subject.PublishSubject;
Subject<String> citiesStream = PublishSubject.create();
citiesStream
.map(city -> "A: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Amersfoort");
citiesStream.onNext("Groningen");
citiesStream
.map(city -> "B: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Utrecht");
// ------ Output ------
// A: Amersfoort
// A: Groningen
// A: Utrecht
// B: Utrecht
BehaviorSubject
BehaviorSubject
import io.reactivex.subject.Subject;
import io.reactivex.subject.BehaviorSubject;
Subject<String> citiesStream = BehaviorSubject.create();
citiesStream
.map(city -> "A: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Amersfoort");
citiesStream.onNext("Groningen");
citiesStream
.map(city -> "B: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Utrecht");
// ------ Output ------
// A: Amersfoort
// A: Groningen
// B: Groningen
// A: Utrecht
// B: Utrecht
ReplaySubject
ReplaySubject
import io.reactivex.subject.Subject;
import io.reactivex.subject.ReplaySubject;
Subject<String> citiesStream = ReplaySubject.create();
citiesStream
.map(city -> "A: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Amersfoort");
citiesStream.onNext("Groningen");
citiesStream
.map(city -> "B: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Utrecht");
// ------ Output ------
// A: Amersfoort
// A: Groningen
// B: Amersfoort
// B: Groningen
// A: Utrecht
// B: Utrecht
AsyncSubject
AsyncSubject
import io.reactivex.subject.Subject;
import io.reactivex.subject.AsyncSubject;
Subject<String> citiesStream = AsyncSubject.create();
citiesStream
.map(city -> "A: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Amersfoort");
citiesStream.onNext("Groningen");
citiesStream
.map(city -> "B: " + city)
.subscribe(System.out::println);
citiesStream.onNext("Utrecht");
citiesStream.onComplete();
// ------ Output ------
// A: Utrecht
// B: Utrecht
Schedulers
"If you want to introduce multithreading into your cascade of Observable operators, you can do so by instructing those operators to operate on particular Schedulers." - Reactivex
Schedulers
- computation()
- io()
- newThread()
- trampoline()
observeOn/subscribeOn
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
Observable
.just("groningen")
.subscribeOn(Schedulers.newThread())
.doOnNext(city -> System.out.println(Thread.currentThread()))
.observeOn(Schedulers.computation())
.doOnNext(city -> System.out.println(Thread.currentThread()))
.switchMap(city -> Observable.fromArray(city.split("")))
.distinct()
.map(StringUtils::capitalize)
.sorted()
.zipWith(Observable.range(1, Integer.MAX_VALUE),
(city, n) ->
String.format("%2d. %s", n, city)
)
.doOnComplete(() ->
System.out.println(Thread.currentThread())
)
.subscribe(System.out::println);
Multithreading
// ---- Output ----
// Thread[RxNewThreadScheduler-1,5,main]
// Thread[RxComputationThreadPool-1,5,main]
// 1. E
// 2. G
// 3. I
// 4. N
// 5. O
// 6. R
// Thread[main,5,main]
RxJS
NgRx
@Injectable()
export class CityService {
public constructor(private httpClient: HttpClient) {
}
public get(): Observable<City> {
return this.httpClient
.get(`/city`);
}
}
@Component({
...
template: `
<p>{{ (city$ | async).name }}</p>
`
})
export class CityComponent implements OnInit {
public city$: Observable<City>;
public constructor(private cityService: CityService) {
}
public ngOnInit(): void {
this.city$ = this.cityService.get();
}
}
Http
@Injectable()
export class CityService {
public city$: Observable<City>;
private citySubject: Subject<City>;
public constructor(private httpClient: HttpClient) {
this.citySubject = new Subject();
this.city$ = this.citySubject.asObservable();
}
public get(): void {
this.httpClient
.get(`/city`)
.subscribe(city => this.citySubject.next(city));
}
}
@Component({
...
template: `
<p>{{ (city$ | async).name }}</p>
`
})
export class CityComponent implements OnInit {
public city$: Observable<City>;
public constructor(private cityService: CityService) {
}
public ngOnInit(): void {
this.city$ = this.cityService.city$;
this.cityService.get();
}
}
ReactiveForms
@Component({
...
template: `
<div>
<h1>Search</h1>
<input type="text" [formControl]="query"/>
<ul>
<li *ngFor="let city of cities$ | async">{{city}}</li>
</ul>
</div>
`
})
export class CityComponent implements OnInit {
public cities$: Observable<City[]>;
public query = new FormControl();
public constructor(private cityService: CityService) {
}
public ngOnInit(): void {
this.cities$ = this.query
.valueChanges
.debounceTime(400)
.distinctUntilChanged()
.switchMap(query => this.cityService.search(query));
}
}
Operators
Hot vs Cold
Questions?
ReactiveX
By rachnerd
ReactiveX
- 402