"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

  • 395