# RxJS Intro ![rxjs](https://rxjs-dev.firebaseapp.com/generated/images/marketing/home/Rx_Logo-512-512.png)
# ME * [Georg M. Sorst](mailto:g.sorst@findologic.com) * [@piefke_schorsch](https://twitter.com/piefke_schorsch) * CTO @ ![FINDOLOGIC(https://findologic.com)](images/findologic.png)
# ReactiveX? > Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change
# What? Implementation of observer pattern
# Observer pattern?
Observable
→ Subscription
→ Observer
![youtuber](images/youtuber.jpg)
→ ![subscribe](images/subscribe.png)
→ ![audience](images/audience.jpg)
# Observer pattern ```javascript /* Observable */ element /* Subscription */ .addEventListener('click', /* Observer */ function() {…}); ```
# Observable? ![youtuber](images/youtuber.jpg) * Anything that emits values: * Promises * DOM events * Timers * Arrays * …
# Observables Everything is a stream ![stream](images/stream.jpg) Even if it just emits a single value ![drop](images/drop.jpg)
# Subscription? ![subscribe](images/subscribe.png) * Compose and transform streams * Filter, map, zip, …
# Observer? ![audience](images/audience.jpg) * Actually does something * Render, upload, store, …
# RxJS > RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using observables that makes it easier to compose asynchronous or callback-based code
# Code pls ```javascript rxjs.fromEvent(element, 'keypress').pipe( map(event => event.target.value), filter(value => value !== ''), ).subscribe(value => console.log(value)); ```
# Straight up pipe ![branching](images/RxJS_no_branching.png)
# Why ReactiveX?
# Functional Operators are pure functions ↓ Clean, stateless code ## `f(x) = y`
# Asynchronous Stateless functions are easy to run asynchronously ↓ Great concurrency ![Sequential Concurrent](images/Sequential_Concurrent.png)
# Versatile Lots of common operators built in ↓ Less code
# Support * ReactiveX has implementations in all major languages * Even ![php](images/php.svg)
# Usage * Great for asynchronous event driven architectures * ![nodejs](images/nodejs.svg) * ![angular](https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/768px-Angular_full_color_logo.svg.png) * Single page apps
# Operator overload 104 operators built in: audit auditTime buffer bufferCount bufferTime bufferToggle bufferWhen catchError combineAll combineLatest concat concatAll concatMap concatMapTo count debounce debounceTime defaultIfEmpty delay delayWhen dematerialize distinct distinctUntilChanged distinctUntilKeyChanged elementAt endWith every exhaust exhaustMap expand filter finalize find findIndex first flatMap groupBy ignoreElements isEmpty last map mapTo materialize max merge mergeAll mergeMap mergeMapTo mergeScan min multicast observeOn onErrorResumeNext pairwise partition pluck publish publishBehavior publishLast publishReplay race reduce refCount repeat repeatWhen retry retryWhen sample sampleTime scan sequenceEqual share shareReplay single skip skipLast skipUntil skipWhile startWith subscribeOn switchAll switchMap switchMapTo take takeLast takeUntil takeWhile tap throttle throttleTime throwIfEmpty timeInterval timeout timeoutWith timestamp toArray window windowCount windowTime windowToggle windowWhen withLatestFrom zip zipAll
# Finding the right operator Use [RxJS Operator Decision Tree](https://rxjs-dev.firebaseapp.com/operator-decision-tree) to find the right operator
# RX Marbles ![take](images/map-rxmarble.webp)
# [take](https://rxmarbles.com/#take)
# [last](https://rxmarbles.com/#last)
# [filter](https://rxmarbles.com/#filter)
# How? Live demo time! ![scared](images/scared.gif)
# Live demo Let's built a Wikipedia autocomplete!
# Requirements 1. Listen to text input events 2. Get search field value 3. Filter out empty queries 4. Debounce rapid typing 5. Discard ongoing requests 6. Display spinner 7. Fetch suggestions 8. Hide spinner 9. Display results
## Listen to text input events Plain
element.addEventListener('input', (event) => {});
RxJS
rxjs.fromEvent(element, 'input').subscribe();
## Get search field value Plain ```javascript let query = event.target.value.trim(); ``` RxJS ```javascript rxjs.operators.map(event => event.target.value.trim() ```
## Filter out empty queries Plain ```javascript if (query === '') { return; } ``` RxJS ```javascript rxjs.operators.filter(query => query !== '') ```
## Debounce rapid typing Plain ```javascript window.clearTimeout(debounceTimeout); debounceTimeout = window.setTimeout(() { /* Handle query */ }, 300); ``` RxJS ```javascript rxjs.operators.debounce(() => rxjs.interval(300)), /* Handle query */ ```
## Discard ongoing requests Plain ```javascript controller.abort(); let controller = new AbortController(); let signal = controller.signal; fetch(baseUrl + query, {signal}).then( /* Handle response */ ); ``` RxJS ```javascript rxjs.operators.switchMap(query => from(fetch(baseUrl + query)), /* Handle response */ ```
## Display spinner Plain ```javascript spinner.style.display = 'block'; ``` RxJS ```javascript rxjs.operators.tap(() => spinner.style.display = 'block') ```
## Fetch suggestions Plain ```javascript fetch(url) .then(response => response.json()) .then(/* Handle response */) ); ``` RxJS ```javascript rxjs.operators.switchMap(query => from(fetch(baseUrl + query)), rxjs.operators.switchMap(response => from(response.json()), /* Handle response */ ```
## Display spinner Plain ```javascript spinner.style.display = 'none'; ``` RxJS ```javascript rxjs.operators.tap(() => spinner.style.display = 'none') ```
## Display results Plain ```javascript fetch(url).then(json => { let titles = json[1]; let links = json[3]; render(container, titles, links); }); ``` RxJS ```javascript let fetchSuggestions = rxjs.operators.switchMap(query => from(fetch(baseUrl + query))); let prepareResults = rxjs.operators.map(json => ({'titles': json[1], 'links': json[3]})); let renderResults = ({titles, links}) => render(container, titles, links); rxjsfromEvent(element, 'input').pipe( fetchSuggestions, preparedResults, ).subscribe(renderResults); ```
# Full version ## Plain * [Demo](demo/plain/) * [Code](demo/plain/js/search.js) ## RxJS * [Demo](demo/rxjs/) * [Code](demo/rxjs/js/search.js)
# Gotchas
# Subscribe! * Observables without subscription do nothing * Just like functions that are not called
# Handle errors! Streams will stop once they throw an error ![complete_error](images/RxJS_complete_error.png) Handle error-prone actions (eg. fetching URL) in sub-stream
# No branching! * Pipes run in a straight line * No conditionals / branching ![branching](images/RxJS_branching.png) ![branching](images/RxJS_no_branching.png)