Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Watch out: Observables are here to stay

Watch out: Observables are here to stay

The web is evolving, we got it. One of the clear consequences is the complexity of our web apps (formerly known as ‘websites’). The conciseness of functional programming and its fundamentals got our attention, but we knew we could do better. And now we have the Reactive programming model, a functional and declarative way of dealing with big amounts of data.

In the center of it we have Observables: objects responsible to keep your application alive, reacting to any mutation your data may have, through any period of time. We’ll take a look on the concepts and also on the lib that implements it in Angular’s core: RxJS. Using the provided operators, we have great power on our hands, doing anything imaginable in a concise, declarative and easy-to-maintain way.

Watch out: observables are here to stay!

Avatar for Guilherme Ventura

Guilherme Ventura

August 30, 2019
Tweet

Other Decks in Programming

Transcript

  1. const position$ = new Observable(observer => { }); const position$

    = new Observable(observer => { const watchId = navigator.geolocation.watchPosition( position => observer.next(position), err => observer.error(err) ); }); const position$ = new Observable(observer => { const watchId = navigator.geolocation.watchPosition( position => observer.next(position), err => observer.error(err) ); return function teardown() { navigator.geolocation.clearWatch(watchId); } });
  2. const subscription = position$.subscribe({ next: position => console.log('On the move:',

    position.coords), error: err => console.error('Could not retrieve location'), complete: () => console.log('No more updates'), }); const subscription = position$.subscribe({ next: position => console.log('On the move:', position.coords), error: err => console.error('Could not retrieve location'), complete: () => console.log('No more updates'), }); // … when we don't need the updates anymore subscription.unsubscribe();
  3. const number$ = new Observable(observer => { observer.next(1); observer.next(2); observer.next(3);

    observer.next(4); observer.next(5); observer.complete(); }); 1. Lazy
  4. const number$ = new Observable(observer => {...}); number$.subscribe(number => console.log(number));

    console.log('The end.'); //> 1 //> ... //> 5 //> "The end." 1. Lazy and synchronous
  5. 2. Cancellable const users$ = http.get('https://api.github.com/users'); const request = users$.subscribe(users

    => renderUsers(users)); // ... user navigated to a different page request.unsubscribe(); // HTTP request is aborted
  6. number$.pipe( filter(number => number % 2 !== 0) ); number$.subscribe(console.log);

    //> 1 //> 2 //> 3 //> 4 //> 5 3. Immutable number$.pipe( filter(number => number % 2 !== 0) ).subscribe(console.log); //> 1 //> 3 //> 5 const odd$ = number$.pipe( filter(number => number % 2 !== 0) ); odd$.subscribe(console.log); //> 1 //> 3 //> 5
  7. 3. Immutable const number$ = of(1, 2, 3, 4, 5);

    const odd$ = number$.pipe( filter(number => number % 2 !== 0) ); const even$ = number$.pipe( filter(number => number % 2 === 0) );
  8. const number$ = of(1, 2, 3, 4, 5); const odd$

    = number$.pipe( filter(number => number % 2 !== 0) ); const even$ = number$.pipe( filter(number => number % 2 === 0) );
  9. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = of(3); const value2$ = of(7);
  10. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = of(3); const value2$ = of(7); const result$ = combineLatest([value1$, value2$]);
  11. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = of(3); const value2$ = of(7); const result$ = combineLatest([value1$, value2$]);
  12. const value1$ = of(3); const value2$ = of(7); const result$

    = combineLatest([value1$, value2$]); combineLatest([value1$, value2$]) Super Summer When either value1 or value2 input changes, update result input with the sum of the two input values. 3 7 [3,7] value1$ value2$ result$
  13. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = of(3); const value2$ = of(7); const result$ = combineLatest([value1$, value2$]);
  14. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = of(3); const value2$ = of(7); const result$ = combineLatest([value1$, value2$]).pipe( map((values) => values[0] + values[1]) );
  15. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = of(3); const value2$ = of(7); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) );
  16. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = of(3); const value2$ = of(7); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(sum => console.log(sum)); const value1$ = of(3); const value2$ = of(7); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(sum => console.log(sum)); //> 10
  17. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = fromEvent(document.getElementById('value1'), 'input'); const value2$ = fromEvent(document.getElementById('value2'), 'input'); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(sum => console.log(sum));
  18. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = fromEvent(document.getElementById('value1'), 'input'); const value2$ = fromEvent(document.getElementById('value2'), 'input'); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(console.log);
  19. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); const value2$ = fromEvent(document.getElementById('value2'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(console.log);
  20. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); const value2$ = fromEvent(document.getElementById('value2'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(console.log);
  21. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), );

    const value2$ = fromEvent(document.getElementById('value2'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(console.log); const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); Super Summer When either value1 or value2 input changes, update result input with the sum of the two input values.
  22. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), );

    const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); Super Summer When either value1 or value2 input changes, update result input with the sum of the two input values. const operator = (source) => new Observable(...); operator anatomy
  23. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), );

    const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); Super Summer When either value1 or value2 input changes, update result input with the sum of the two input values. const operator = (source) => new Observable(...); const map = (transformFn) => (source) => new Observable(...); operator anatomy
  24. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), );

    Super Summer When either value1 or value2 input changes, update result input with the sum of the two input values. const map = (transformFn) => (source) => new Observable(...); const toValue = map(event => parseInt(event.target.value, 10)); const value1$ = toValue(fromEvent(...)); operator anatomy
  25. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), );

    Super Summer When either value1 or value2 input changes, update result input with the sum of the two input values. const map = (transformFn) => (source) => new Observable(...); const toValue = map(event => parseInt(event.target.value, 10)); const value1$ = toValue(fromEvent(...)); const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe(toValue); operator anatomy
  26. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), );

    const map = (transformFn) => (source) => new Observable(...); const toValue = map(event => parseInt(event.target.value, 10)); const value1$ = toValue(fromEvent(...)); const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe(toValue); operator anatomy Super Summer When either value1 or value2 input changes, update result input with the sum of the two input values. const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); const value2$ = fromEvent(document.getElementById('value2'), 'input').pipe( map(event => parseInt(event.target.value, 10)), ); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(console.log);
  27. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const toValue = map(event => parseInt(event.target.value, 10)); const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe(toValue); const value2$ = fromEvent(document.getElementById('value2'), 'input').pipe(toValue); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(console.log);
  28. Super Summer When either value1 or value2 input changes, update

    result input with the sum of the two input values. const toValue = map(event => parseInt(event.target.value, 10)); const value1$ = fromEvent(document.getElementById('value1'), 'input').pipe(toValue); const value2$ = fromEvent(document.getElementById('value2'), 'input').pipe(toValue); const result$ = combineLatest([value1$, value2$]).pipe( map(([value1, value2]) => value1 + value2) ); result$.subscribe(result => { document.getElementById('result').value = String(result); });
  29. Takeaways • RxJS operators are factories • Stream creation helpers

    const click$ = fromEvent(document, 'click'); ◦ fromEvent
  30. Takeaways • RxJS operators are factories • Stream creation helpers

    ◦ fromEvent ◦ of const numbers$ = of(1, 2, 3, 4, 5);
  31. Takeaways • RxJS operators are factories • Stream creation helpers

    ◦ fromEvent ◦ of const readFile = bindNodeCallback(fs.readFile); const file$ = readFile('./rxjs.rocks'); ◦ bindCallback/bindNodeCallback
  32. Takeaways • RxJS operators are factories • Stream creation helpers

    ◦ fromEvent ◦ of ◦ bindCallback/bindNodeCallback ◦ from const tech$ = from(['angular', 'rxjs']); const value$ = from(function* generator() {/*...*/}); const fetch$ = from(fetch('http://example.org'));
  33. Takeaways • RxJS operators are factories • Stream creation helpers

    ◦ fromEvent ◦ of ◦ bindCallback/bindNodeCallback ◦ from const null$ = defer(() => of(null)); ◦ defer
  34. Promise hot tip: from vs defer function fetchUsers() { return

    fetch('https://api.github.com/users') .then(response => response.json()); } const users$ = from(fetchUsers()); // Request was already made ☹ users$.subscribe(data => renderUsers(usersList));
  35. Promise hot tip: from vs defer function fetchUsers() { return

    fetch('https://api.github.com/users') .then(response => response.json()); } const users$ = from(fetchUsers()); // Request was already made ☹ users$.subscribe(data => renderUsers(usersList)); function fetchUsers() { return fetch('https://api.github.com/users') .then(response => response.json()); } const users$ = from(fetchUsers()); const users$ = defer(() => fetchUsers()); users$.subscribe(data => renderUsers(usersList)); // function fetchUsers() { return fetch('https://api.github.com/users') .then(response => response.json()); } const users$ = from(fetchUsers()); const users$ = defer(() => fetchUsers()); users$.subscribe(data => renderUsers(usersList)); //
  36. Summary • Everything can be a stream • Observables are

    just functions • RxJS is best used to coordinate events • Write down your problems, and draw observables @danguilherme
  37. OBSERVABLES ARE HERE TO STAY WATCH OUT Photo by Hal

    Gatewood on Unsplash @danguilherme