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

Bending time with RxJS

Bending time with RxJS

A pratical introduction to RxJS. Presentation given at the Reactive Amsterdam meetup on the 2nd December of 2015

B083b8207ccd0744a5abb18c8e75d24d?s=128

Sergi Mansilla

December 03, 2015
Tweet

More Decks by Sergi Mansilla

Other Decks in Programming

Transcript

  1. Bending time with RxJS A practical introduction to Reactive Extensions

    by sergi mansilla | @sergimansilla Reactive Amsterdam, 2nd December 2015
  2. @sergimansilla

  3. http://github.com/sergi

  4. Previously

  5. None
  6. https://pragprog.com/book/smreactjs/reactive-programming-with-rxjs

  7. Human beings have hard-wired time in their brain

  8. async / sync

  9. Callbacks Promises Generators Events

  10. Concurrency is hard

  11. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } });
  12. Why are we still micromanaging code? Um yeah…about these event

    callbacks
  13. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } });
  14. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } });
  15. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } });
  16. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } });
  17. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2 && isAPressed) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } }); var isAPressed = false; document.addEventListener('keydown', e => { isAPressed = e.keyCode === 65; }, false); document.addEventListener('keyup', e => { isAPressed = false; }, false);
  18. We code the how instead of the what

  19. Programming should be about the what

  20. State is dangerous

  21. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2 && isAPressed) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } }); var isAPressed = false; document.addEventListener('keydown', e => { isAPressed = e.keyCode === 65; }, false); document.addEventListener('keyup', e => { isAPressed = false; }, false);
  22. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } }); Where is my handler??
  23. Event limbo

  24. Isn’t that the problem promises try to solve?

  25. var y = f(x); var z = g(y); fAsync(x).then(...); gAsync(y).then(...);

    res = stocks // array .filter(q => q.symbol == 'FB') .map(q => q.quote) res.forEach(x => ... res = stocks // async anything .filter(q => q.symbol == 'FB') .map(q => q.quote) res.subscribe(x => ... Sync Sync Promises RxJS
  26. Click! … … Click! Click!

  27. Click! , , Click! Click! [ ]

  28. None
  29. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    .filter(n => n % 2) .map(n => 'item ' + n) .forEach(n => console.log(n)) // "item 1" // "item 3" // "item 5" // "item 7" // "item 9"
  30. Reactive Programming

  31. None
  32. None
  33. Deal with values that change over time

  34. None
  35. RxJS helps us compose asynchronous and event-based programs

  36. The Observable a universal datatype?

  37. var clicks = 0; document.addEventListener('click', function register(e) { if (clicks

    < 10) { if (e.clientX > innerWidth / 2) { console.log(e.clientX, e.clientY); clicks += 1; } } else { document.removeEventListener('click', register); } }); Filter Limit to 10 Print the coordinates
  38. fromEvent(document, 'click') .filter(c => c.clientX > innerWidth / 2 })

    .take(10) .subscribe(c => console.log(c.clientX, c.clientY) })
  39. fromEvent(document, 'click') .filter(c => c.clientX > innerWidth / 2 })

    .take(10) .subscribe(c => console.log(c.clientX, c.clientY) }) Create Observable
  40. fromEvent(document, 'click') .filter(c => c.clientX > innerWidth / 2 })

    .take(10) .subscribe(c => console.log(c.clientX, c.clientY) }) Create filtered Observable
 from the first one
  41. fromEvent(document, 'click') .filter(c => c.clientX > innerWidth / 2 })

    .take(10) .subscribe(c => console.log(c.clientX, c.clientY) }) Create final Observable
 taking only first 10 results
  42. fromEvent(document, 'click') .filter(c => c.clientX > innerWidth / 2 })

    .take(10) .subscribe(c => console.log(c.clientX, c.clientY) }) Actually kick off computation
  43. First class async (Holy sh*t!)

  44. Observable Observer pattern Iterator pattern

  45. Rx.Observable - OnNext() - OnError() - OnComplete()

  46. // Creates an observable sequence of 5 integers var source

    = Rx.Observable.range(1, 5) // Prints out each item var subscription = source.subscribe( x => { console.log('onNext: ' + x) }, e => { console.log('onError: ' + e.message) }, () => { console.log('onCompleted') }) // => onNext: 1 // => onNext: 2 // => onNext: 3 // => onNext: 4 // => onNext: 5 // => onCompleted
  47. function getDrag(el) { return Dom.mousedown(el) .flatMap(md => Dom.mousemove(document)) .takeUntil(Dom.mouseup(document)); }

    getDrag() .subscribe(e => log(e.clientX, e.clientY));
  48. // Search Wikipedia for a given term function searchWikipedia(term) {

    var cleanTerm = global.encodeURIComponent(term); var url = 'http://en.wikipedia.org/w/api.php?...'; return Rx.Observable.getJSONPRequest(url); } // Get all distinct key up events from the input and var keyup = fromEvent(input, 'keyup') .map(e => e.target.value) .where(text => text.length > 2) // Longer than 2 chars .throttle(200) // Pause for 200ms .distinctUntilChanged(); // Only if the value has changed var doSearch = keyup .flatMap(text => searchWikipedia(text)) // Search wikipedia .switch() // Ensure no out of order results doSearch.subscribe(results => { // Do stuff with the results! });
  49. None
  50. No external state

  51. // Search Wikipedia for a given term function searchWikipedia(term) {

    return fromArray(['JavaScript', 'JavaServer Pages', 'JavaSoft', 'JavaScript library', 'JavaScript Object Notation', 'JavaScript engine', 'JavaScriptCore']); } // Get all distinct key up events from the input and var keyup = fromEvent(input, 'keyup') .map(e => e.target.value) .where(text => text.length > 2) // Longer than 2 chars .throttle(200) // Pause for 200ms .distinctUntilChanged(); // Only if the value has changed
  52. filter { }

  53. map { } 1 2 3 4 5 1 4

    6 8 10
  54. 0 1 merge 1 0 0 1 A B C

    3 2 0 1 3 2 100ms 200ms
  55. throttleFirst(300) 1 2 3 1 3 300ms 4 5 6

    300ms 7 7
  56. Flat A A 1 A 2 A 3 FlatMap

  57. fromArray fromCallback fromEvent fromEventPattern fromIterable fromNodeCallback fromPromise

  58. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    .filter(n => n % 2) .map(n => 'item ' + n) .forEach(n => console.log(n)) // "item 1" // "item 3" // "item 5" // "item 7" // "item 9"
  59. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    .filter(n => n % 2) .map(n => 'item ' + n) .forEach(n => console.log(n)) // "item 1" // "item 3" // "item 5" // "item 7" // "item 9" loop loop loop
  60. None
  61. fromArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

    .filter(n => n % 2) .map(n => n * 100) .map(n => 'item ' + n) .subscribe(n => console.log(n))
  62. Only scratching the surface

  63. Only scratching the surface Schedulers Async testing
 Libraries

  64. None
  65. /** @jsx hJSX */ import Cycle from '@cycle/core'; import {makeDOMDriver,

    hJSX} from '@cycle/dom'; function main(drivers) { return { DOM: drivers.DOM.select('input').events('click') .map(ev => ev.target.checked) .startWith(false) .map(toggled => <div> <input type="checkbox" /> Toggle me <p>{toggled ? 'ON' : 'off'}</p> </div> ) }; } let drivers = { DOM: makeDOMDriver('#app') }; Cycle.run(main, drivers);
  66. None
  67. https://pragprog.com/book/smreactjs/reactive-programming-with-rxjs

  68. Thanks! @sergimansilla