à la découverte des observables - humantalks paris 2017
TRANSCRIPT
![Page 1: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/1.jpg)
@nicoespeon
À la découverte des Observables
Meetup HumanTalks Paris Décembre 2017
1
![Page 2: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/2.jpg)
@nicoespeon 2
![Page 3: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/3.jpg)
@nicoespeon 3
![Page 4: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/4.jpg)
@nicoespeon 4
Back-end
1
23
![Page 5: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/5.jpg)
@nicoespeon 5
$search.on('click', () !=> { fetchTrips() .then(({data}) !=> renderFoundTrips(data))
.catch(handleError) })
![Page 6: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/6.jpg)
@nicoespeon 6
![Page 7: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/7.jpg)
@nicoespeon 7
Back-end
1
3
2
4
Si c’est incomplet, on « poll »
![Page 8: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/8.jpg)
@nicoespeon 8
$search.on('click', () !=> { fetchTrips() .then(renderFoundTripsAndPoll) .catch(handleError) })
function renderFoundTripsAndPoll({data, isComplete}) { renderFoundTrips(data) if (!isComplete) { pollTrips() .then(renderFoundTripsAndPoll) .catch(handleError) } }
![Page 9: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/9.jpg)
@nicoespeon 9
![Page 10: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/10.jpg)
@nicoespeon 10
Back-end
1
3
2
4
On optimise le polling
5s
![Page 11: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/11.jpg)
@nicoespeon 11
$search.on('click', () !=> { fetchTrips() .then(renderFoundTripsAndPoll) .catch(handleError) })
function renderFoundTripsAndPoll({data, isComplete}) { const DELAY_BEFORE_POLLING_IN_MS = 5000
renderFoundTrips(data) if (!isComplete) { setTimeout(() !=> { pollTrips() .then(renderFoundTripsAndPoll) .catch(handleError) }, DELAY_BEFORE_POLLING_IN_MS ) } }
![Page 12: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/12.jpg)
@nicoespeon
« Et si on lançait la recherche au fur et à
mesure de la saisie ? »
12
![Page 13: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/13.jpg)
@nicoespeon 13
const DEBOUNCE_TIME_IN_MS = 250 let fetchTripsTimeout
$input.on('keypress', () !=> { clear(fetchTripsTimeout) fetchTripsTimeout = setTimeout(() !=> { fetchTrips() .then(renderFoundTripsAndPoll) .catch(handleError) }, DEBOUNCE_TIME_IN_MS); })
function renderFoundTripsAndPoll({data, isComplete}) { const DELAY_BEFORE_POLLING_IN_MS = 5000 renderFoundTrips(data) if (!isComplete) { setTimeout(() !=> { pollTrips() .then(renderFoundTripsAndPoll) .catch(handleError) }, DELAY_BEFORE_POLLING_IN_MS ) } }
![Page 14: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/14.jpg)
@nicoespeon 14
const DEBOUNCE_TIME_IN_MS = 250 let fetchTripsTimeout
$input.on('keypress', () !=> { clear(fetchTripsTimeout) fetchTripsTimeout = setTimeout(() !=> { fetchTrips() .then(renderFoundTripsAndPoll) .catch(handleError) }, DEBOUNCE_TIME_IN_MS); })
function renderFoundTripsAndPoll({data, isComplete}) { const DELAY_BEFORE_POLLING_IN_MS = 5000 renderFoundTrips(data) if (!isComplete) { setTimeout(() !=> { pollTrips() .then(renderFoundTripsAndPoll) .catch(handleError) }, DELAY_BEFORE_POLLING_IN_MS ) } }
![Page 15: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/15.jpg)
@nicoespeon 15
Back-end
1
2
3
Nous avons un bug…
4
Oups
![Page 16: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/16.jpg)
@nicoespeon 16
![Page 17: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/17.jpg)
@nicoespeon
Nicolas Carlo
17
![Page 18: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/18.jpg)
@nicoespeon
Servez-vous des Observables pour gérer des scénarios asynchrones compliqués
18
![Page 19: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/19.jpg)
@nicoespeon
Si on modélisait ça autrement ?
19
Un événement
Une erreur
Flux terminé
temps
= Observable
![Page 20: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/20.jpg)
@nicoespeon
- Andre Staltz, 2 minute introduction to Rx
« Think of it as an asynchronous
immutable array »
20
![Page 21: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/21.jpg)
@nicoespeon
Utilisez les Observables avec ReactiveX
• Projet porté par Microsoft, en open-source depuis 2012
• Implémente les Observables dans de nombreux langages (Java, Python, JS, .NET, Go, Ruby, Elixir, Swift…)
• Fournit une API pour manipuler des flux observables
21
![Page 23: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/23.jpg)
@nicoespeon 23
![Page 24: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/24.jpg)
@nicoespeon
clickStream ➜ click$
24
![Page 25: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/25.jpg)
@nicoespeon
Fetch au click
25
flatMap
click$
fetchedTrips$
![Page 26: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/26.jpg)
@nicoespeon 26
const fetchedTrips$ = fromEvent($search, 'click') .flatMap(fetchTrips)
fetchedTrips$.subscribe( ({data}) !=> renderFoundTrips(data), handleError )
![Page 27: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/27.jpg)
@nicoespeon
Le polling
27
flatMap
isRequestComplete$
polledTrips$
filter
![Page 28: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/28.jpg)
@nicoespeon 28
const isRequestComplete$ = new Subject()
const fetchedTrips$ = fromEvent($search, 'click') .flatMap(fetchTrips)
const polledTrips$ = isRequestComplete$ .filter((isComplete) !=> !isComplete) .flatMap(pollTrips)
const foundTrips$ = merge(fetchedTrips$, polledTrips$)
foundTrips$.subscribe( ({data, isComplete}) !=> { renderFoundTrips(data) isRequestComplete$.next(isComplete) }, handleError )
![Page 29: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/29.jpg)
@nicoespeon
Le polling optimisé
29
flatMap
isRequestComplete$
polledTrips$
delay
![Page 30: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/30.jpg)
@nicoespeon 30
const isRequestComplete$ = new Subject()
const fetchedTrips$ = fromEvent($search, 'click') .flatMap(fetchTrips)
const DELAY_BEFORE_POLLING_IN_MS = 5000 const polledTrips$ = isRequestComplete$ .filter((isComplete) !=> !isComplete) .delay(DELAY_BEFORE_POLLING_IN_MS ) .flatMap(pollTrips)
const foundTrips$ = merge(fetchedTrips$, polledTrips$)
foundTrips$.subscribe( ({data, isComplete}) !=> { renderFoundTrips(data) isRequestComplete$.next(isComplete) }, handleError )
![Page 31: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/31.jpg)
@nicoespeon
« Et si on lançait la recherche au fur et à
mesure de la saisie ? »
31
![Page 32: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/32.jpg)
@nicoespeon
Fetch au fur et à mesure
32
flatMap
keypress$
fetchedTrips$
debounce
![Page 33: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/33.jpg)
@nicoespeon 33
const isRequestComplete$ = new Subject()
const DEBOUNCE_TIME_IN_MS = 250 const fetchedTrips$ = fromEvent($input, 'keypress') .debounce(DEBOUNCE_TIME_IN_MS) .flatMap(fetchTrips)
const DELAY_BEFORE_POLLING_IN_MS = 5000 const polledTrips$ = isRequestComplete$ .filter((isComplete) !=> !isComplete) .delay(DELAY_BEFORE_POLLING_IN_MS ) .flatMap(pollTrips)
const foundTrips$ = merge(fetchedTrips$, polledTrips$)
foundTrips$.subscribe( ({data, isComplete}) !=> { renderFoundTrips(data) isRequestComplete$.next(isComplete) }, handleError )
![Page 34: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/34.jpg)
@nicoespeon 34
![Page 35: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/35.jpg)
@nicoespeon
FlatMapLatest
35
reactivex.io/documentation/operators/flatmap.html
![Page 36: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/36.jpg)
@nicoespeon 36
const isRequestComplete$ = new Subject()
const DEBOUNCE_TIME_IN_MS = 250 const fetchedTrips$ = fromEvent($input, 'keypress') .debounce(DEBOUNCE_TIME_IN_MS) .flatMapLatest(fetchTrips)
const DELAY_BEFORE_POLLING_IN_MS = 5000 const polledTrips$ = isRequestComplete$ .filter((isComplete) !=> !isComplete) .delay(DELAY_BEFORE_POLLING_IN_MS ) .flatMapLatest(pollTrips)
const foundTrips$ = merge(fetchedTrips$, polledTrips$)
foundTrips$.subscribe( ({data, isComplete}) !=> { renderFoundTrips(data) isRequestComplete$.next(isComplete) }, handleError )
![Page 37: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/37.jpg)
@nicoespeon 37
const isRequestComplete$ = new Subject()
const DEBOUNCE_TIME_IN_MS = 250 const fetchedTrips$ = fromEvent($input, 'keypress') .debounce(DEBOUNCE_TIME_IN_MS) .flatMapLatest(fetchTrips)
const DELAY_BEFORE_POLLING_IN_MS = 5000 const polledTrips$ = isRequestComplete$ .filter((isComplete) !=> !isComplete) .delay(DELAY_BEFORE_POLLING_IN_MS ) .flatMapLatest(pollTrips)
const foundTrips$ = merge(fetchedTrips$, polledTrips$)
foundTrips$.subscribe( ({data, isComplete}) !=> { renderFoundTrips(data) isRequestComplete$.next(isComplete) }, handleError )
![Page 38: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/38.jpg)
@nicoespeon
Qu’avons-nous gagné ?
✓ Code déclaratif, facile à faire évoluer
✓ Logique applicative sans effets de bords, facile à tester
✓ Observables composables, facile à ré-utiliser
38
![Page 39: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/39.jpg)
@nicoespeon
L’usage des Observables se popularise dans le web
• Embarqué par défaut dans Angular
• Il y a des adaptations pour chaque librairie : redux−observable, vue-rx…
• Cycle.js est un framework fonctionnel et réactif, basé sur l’usage des Observables
39
![Page 40: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/40.jpg)
@nicoespeon
Pensez aux Observables pour résoudre vos
problèmes asynchrones non triviaux
40
![Page 41: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/41.jpg)
@nicoespeon 41
Bienvenue sur la route de la programmation réactive !
![Page 42: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/42.jpg)
@nicoespeon
Pour aller plus loinThe introduction to Reactive Programming you've been missing
RxJS Functions with Examples
Using Observables in real life
Testing reactive code
📚 Reactive Programming - What is RxJS? (lessons)
🎦 The whole future declared in a var (video)
📑 Dynamics of Change - Why Reactivity matters (PDF)
🥋 Reactive Extensions (Rx.js) Workshop (kata)
42
![Page 43: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/43.jpg)
@nicoespeon
Des diagrammes Marbles dans le code
43
const polledTrips$ = isRequestComplete$ !// !!---(true)----(false)----------(false)-------> .filter((isComplete) !=> !isComplete) !// ------------(false)----------(false)--------> .delay(DELAY_BEFORE_POLLING_IN_MS ) !// ----------------(false)----------(false)----> .flatMapLatest(pollTrips) !// ----------------(A)--------------(A)-------->
![Page 44: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/44.jpg)
@nicoespeon
Analysons les strates du code
44
Gestion du temps (debounce, délai)
Logique applicative
Effets de bord (rendu, gestion des erreurs)
Évènements du DOM (click, keypress)
![Page 45: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/45.jpg)
@nicoespeon 45
const DEBOUNCE_TIME_IN_MS = 250 let fetchTripsTimeout
$input.on('keypress', () !=> { clear(fetchTripsTimeout) fetchTripsTimeout = setTimeout(() !=> { fetchTrips() .then(renderFoundTripsAndPoll) .catch(handleError) }, DEBOUNCE_TIME_IN_MS); })
function renderFoundTripsAndPoll({data, isComplete}) { const DELAY_BEFORE_POLLING_IN_MS = 5000 renderFoundTrips(data) if (!isComplete) { setTimeout(() !=> { pollTrips() .then(renderFoundTripsAndPoll) .catch(handleError) }, DELAY_BEFORE_POLLING_IN_MS ) } }
![Page 46: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/46.jpg)
@nicoespeon 46
const isRequestComplete$ = new Subject()
const DEBOUNCE_TIME_IN_MS = 250 const fetchedTrips$ = fromEvent($input, 'keypress') .debounce(DEBOUNCE_TIME_IN_MS) .flatMapLatest(fetchTrips)
const DELAY_BEFORE_POLLING_IN_MS = 5000 const polledTrips$ = isRequestComplete$ .filter((isComplete) !=> !isComplete) .delay(DELAY_BEFORE_POLLING_IN_MS ) .flatMapLatest(pollTrips)
const foundTrips$ = merge(fetchedTrips$, polledTrips$)
foundTrips$.subscribe( ({data, isComplete}) !=> { renderFoundTrips(data) isRequestComplete$.next(isComplete) }, handleError )
![Page 47: À la découverte des Observables - HumanTalks Paris 2017](https://reader033.vdocument.in/reader033/viewer/2022051521/5aac90467f8b9a435e8b4e41/html5/thumbnails/47.jpg)
@nicoespeon 47
14 alternances
Effets de bords couplés à la logique applicative
6 alternances
Effets de bords isolés