RxJS: Mastering the Operators
What are Operators?
- Pure functions that transform information in the Observable stream.
- Create a new Observable, often based on the current Observable.
- Allow for complex asynchronous code to be easily composed in a declarative manner.
- Methods of the Observable class.
Observable Generators
-
Rx.Observable.<operator>
- Often create a new Observable without any input.
Instance Operators?
-
Rx.Observable.prototype.<operator>
- The this scope is the input Observable.
Composable?
- All operators return an Observable, making them chainable (or pipeable, but not lettable).
- Subscribing to the output Observable will also subscribe to the input Observable.
Versions?
- Stable release: 5.5.6
- Alpha release: 6.0.0-alpha.3
Can I import all operators
- Yes, but you likely do not need all of them.
- Be kind to your users.
Import a single operator?
import from "rxjs/add/operators/switchMap";
import { switchMap } from "rxjs/operators";
or
How do I chain operators in v5.4.x?
class PostsComponent { private user: Observable<User>; ngOnInit() { this.posts = this.user .map(user => user.id) .switchMap(id => this.postsService.getPosts(id) ); } }
How do I pipe operators in v5.5.x?
class PostsComponent { private user: Observable<User>; ngOnInit() { this.posts = this.user.pipe( map(user => user.id), switchMap(id => this.postsService.getPosts(id) ) ); } }
Ok, what about Angular?
- Angular <3 Observables
- Asynchronous tasks in Angular return an Observable.
- The AsyncPipe is your friend.
How many?
75!
How do I choose?
- Use the "Find the right operator" wizard at: http://reactivex.io/rxjs/
- Marble diagrams.
Overview
- Lazy-loaded modules
- Angular Material
- CoreModule for interceptors, models and services
- StateModule with NgRX actions, effects and reducers
- Stateful (smart) containers
- Stateless (dumb) components
git checkout ngrx-refactor-2 cd server && yarn serve cd client && ng serve
https://github.com/blove/ngrx-tour-of-heros
json-server
cd server yarn serve
Let's dive in.
map()
- One of top 5 operators in use
- Data stream transformation operator
- Akin to Array.prototype.map
map()
src/app/+powers/containers/power/power.component.ts
filter()
- One of top 5 operators in use
- Data stream filtration operator
- Akin to Array.prototype.filter
filter()
src/app/+heroes/components/add-hero-dialog.component.ts
src/app/+powers/containers/power/power.component.ts
tap()/.do()
app/+powers/containers/edit/edit.component.ts
- One of top 5 operators in use
- Allows stream to be observed sideband
- Can perform side effects with observed data
- Does not modify the stream in any way
- Can also observe error and complete
- Useful for doing work that reacts to the stream but otherwise does not handle the stream
tap()/.do()
app/+powers/containers/edit/edit.component.ts
switchMap()
src/app/+heroes/containers/edit/edit.component.ts
- One of top 5 operators in use
- Another transformation operator
- Switches from one stream to another
- Unsubscribes from previous Observable
- Subscribes to new Observable
src/app/state/powers/effects/powers.ts
switchMap()
- One of top 5 operators in use
- Another transformation operator
- Switches from one stream to another
- Unsubscribes from previous Observable
- Subscribes to new Observable
catchError()/.catch()
app/state/powers/effects/powers.ts
first()
app/+powers/containers/edit/edit.component.ts
- Returns the first value observed and completes stream
- Another filtration operator
- Allow a predicate to be used to filter for first desired value
- Allows a selector to be used to transform returned value
- Supports optional default value
- Useful for once-and-done handlers
first()
app/+powers/containers/edit/edit.component.ts
last()
- Returns the last value observed and completes stream
- Another filtration operator
- Allow a predicate to be used to filter for first desired value
- Last will wait for completion from original stream before returning
- Useful for once-and-done handlers
last()
take()
- Returns the first N values observed and completes stream
- Another filtration operator
take()
skip()
- Skips the first N values observed then continues to emit any additional values
- Another filtration operator
- Often used with Replay and Behavior subjects to skip unwanted values
- Synonymous with .filter((val, index) => index >= N)
skip()
startWith()
- Emits specified value or values into the stream first
- Continues emitting values from source observable
- Accepts optional scheduler for scheduling emissions
startWith()
withLatestFrom()
- Includes latest value from another Observable once both have emitted only when source emits
- Provides both values (from source and other observables) in array argument
- Can use project function to transform
- Simplest way to join streams of data
- .subscribe(([orig, latest]) => {...})
withLatestFrom()
src/app/+powers/containers/power/power.component.ts
distinctUntilChanged()
src/app/+heroes/components/add-hero-dialog.component.ts
- Only emits distinct values from source
- Cross between filtration and rate limiting operator
- Allows comparator function to be used to determine uniqueness
- Uses strict equality check by default if no comparator provided
- Companions: distinct(), distinctUntilKeyChanged()
src/app/+powers/components/edit-power-dialog.component.ts
distinctUntilChanged()
src/app/+heroes/components/add-hero-dialog.component.ts
- Only emits distinct values from source
- Cross between filtration and rate limiting operator
- Allows comparator function to be used to determine uniqueness
- Uses strict equality check by default if no comparator provided
- Companions: distinct(), distinctUntilKeyChanged()
src/app/+powers/components/edit-power-dialog.component.ts
debounceTime()
- Only emits values once a given interval
- Data stream rate limiting operator
- Drops previous value if new value arrives before interval
- Defaults to milliseconds
- Allows scheduler to be supplied to configure what the dueTime is interpreted as (if you need other than milliseconds)
debounceTime()
src/app/+heroes/components/add-hero-dialog/add-hero-dialog.component.ts
concat()
- Concatenates any number of observables in order
- Data stream creation operator
- If any observable does not complete, subsequent are never subscribed to
- Allows scheduler to be supplied to control emission of notifications
concat()
forkJoin()
src/app/+heroes/containers/character/character.component.ts
- Forks by starting multiple observers at once
- Joins final value from each observable when they complete
- Data stream creation operator
- If any input observable never completes, forkJoin never completes
reduce()
- Accumulate or transform values upon completion notification
- Data stream transformation operator
- Can be used to manage & maintain state
- Like Array.prototype.reduce
- See scan() for streaming version
reduce()
scan()
- Reduce data over time
- Data stream transformation operator
- Can be used to manage & maintain state
- Accumulate over time
- Transform over time
- Like Array.prototype.reduce, except streaming and continuous
- See reduce() for non-streaming version
scan()
Thanks!
- Jon Rista @ BrieBug
- Ben Lesh
- Angular Community
Mastering the Operators
By Brian Love
Mastering the Operators
Learn to master many of the RxJS operators that enable Angular developers to code reactively.
- 1,123