highlighter | lineNumbers | layout | title | titleTemplate | favicon | transition | css |
---|---|---|---|---|---|---|---|
shiki |
true |
cover |
RxJS fundamentals |
%s |
slide-left |
unocss |
ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming.
- An implementation of reactive programming
- Created by Microsoft in 2011 for the .NET framework (Rx.NET)
- Open sourced in 2012
- RxJS was first released in 2013 (?)
- Shipped with Angular since 2015
- Implemented for many languages and frameworks
Promise
.resolve(1)
.then((value) => {
console.log(value)
})
import { of, tap } from 'rxjs'
of(1)
.pipe(
tap((value) => {
console.log(value)
})
)
.subscribe()
[1, 2, 3]
.map((n) => n * 2)
.filter((n) => n < 5)
.reduce((acc, n) => acc + n, 0)
import { from, map, filter, reduce } from 'rxjs'
from([1, 2, 3])
.pipe(
map((n) => n * 2),
filter((n) => n < 5),
reduce((acc, n) => acc + n, 0)
)
.subscribe()
import { of, map, filter } from 'rxjs'
const subscription = of() // a "creation" function
.pipe(
map(), // operators
filter(),
// …
)
.subscribe() // nothing happens until this is called
// later:
subscription.unsubscribe() // clean up to prevent memory leaks
import { of } from 'rxjs'
of(1, 2, 3).subscribe({ // subscribe() accepts an observer
next: (n) => { // next() is called for each value
console.log('Next:', n)
},
complete: () => { // complete() is called without arguments
console.log('Done!')
},
error: (err) => { // in case of an error
console.error(err)
}
})
// Next: 1
// Next: 2
// Next: 3
// Done!
import { fromEvent, interval, merge, combineLatest, zip, withLatestFrom } from 'rxjs'
const clicks$ = fromEvent(document, 'click')
const seconds$ = interval(1000)
merge(clicks$, seconds$).subscribe((clickOrSecond) => {
console.log(clickOrSecond) // either a click or an interval
})
combineLatest([clicks$, seconds$]).subscribe(([latestClick, latestSecond]) => {
console.log(latestClick, latestSecond) // latest click and second when either emits
})
zip(clicks$, seconds$).subscribe(([latestClick, latestSecond]) => {
console.log(latestClick, latestSecond) // latest click and second when both have emitted
})
clicks$
.pipe(withLatestFrom(seconds$))
.subscribe(([latestClick, latestSecond]) => {
console.log(latestClick, latestSecond) // latest click and latest second for each click
})
flowchart LR
ticks["Game loop"] --> next
subgraph Entities
clicks$ --> ball$
mousemoves$ --> paddle$
ball$ & paddle$ & bricks$ & lives$ & score$
end
subgraph Renderers
direction LR
renderBall & renderPaddle & renderBricks & renderLives & renderScore
end
Entities --> next["nextState()"] --> render["renderState()"]
render --> Renderers
Consider the relation between observable and observer:
import { Observable } from 'rxjs'
// same as: `of('Hello')`
const observable$ = new Observable((observer) => {
observer.next('Hello')
observer.complete()
})
const observer = {
next: (value) => console.log(value),
complete: () => console.log('Done!'),
}
observable$.subscribe(observer)
// Hello
// Done!
A Subject is an observable and an observer:
import { Subject } from 'rxjs'
const subject = new Subject()
const observer = {
next: (value) => console.log(value),
complete: () => console.log('Done!'),
}
subject.subscribe(observer)
subject.next('Hello')
// Hello
subject.complete()
// Done!
import { Subject, map, of } from 'rxjs'
const subject = new Subject()
const transform = map(value => value * 2)
const observer = (value) => console.log(value)
subject
.pipe(transform)
.subscribe(observer)
const numbers$ = of(1, 2, 3)
numbers$.subscribe(subject)
// 2
// 4
// 6
subject.next(4)
// 8
flowchart TB
numbers$ --> subject
subject -- transform --> observer
subject --> subject
A Subject with a "current value". Ideal for reading/writing state.
import { BehaviorSubject } from 'rxjs'
const state = new BehaviorSubject(0)
state.subscribe(value => console.log(`A: ${value}`))
// A: 0
state.next(1)
// A: 1
state.subscribe(value => console.log(`B: ${value}`))
// B: 1
state.next(2)
// A: 2
// B: 2
state.value
// 2
flowchart LR
ticks["Game loop"] --> next
subgraph Entities
clicks$ --> ball$
mousemoves$ --> paddle$
ball$ & paddle$ & bricks$ & lives$ & score$
end
subgraph Renderers
direction LR
renderBall & renderPaddle & renderBricks & renderLives & renderScore
end
Entities --> next["nextState()"] --> update["updateState()"] --> render["renderState()"]
update --> Entities
render --> Renderers