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

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

    View Slide

  2. @sergimansilla

    View Slide

  3. http://github.com/sergi

    View Slide

  4. Previously

    View Slide

  5. View Slide

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

    View Slide

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

    View Slide

  8. async / sync

    View Slide

  9. Callbacks
    Promises
    Generators
    Events

    View Slide

  10. Concurrency is hard

    View Slide

  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);
    }
    });

    View Slide

  12. Why are we still
    micromanaging
    code?
    Um yeah…about these
    event callbacks

    View Slide

  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);
    }
    });

    View Slide

  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);
    }
    });

    View Slide

  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);
    }
    });

    View Slide

  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);
    }
    });

    View Slide

  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);

    View Slide

  18. We code the how
    instead of the what

    View Slide

  19. Programming
    should be about
    the what

    View Slide

  20. State is dangerous

    View Slide

  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);

    View Slide

  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??

    View Slide

  23. Event limbo

    View Slide

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

    View Slide

  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

    View Slide

  26. Click!
    … …
    Click!
    Click!

    View Slide

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

    View Slide

  28. View Slide

  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"

    View Slide

  30. Reactive
    Programming

    View Slide

  31. View Slide

  32. View Slide

  33. Deal with values
    that change
    over time

    View Slide

  34. View Slide

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

    View Slide

  36. The Observable
    a universal datatype?

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  42. fromEvent(document, 'click')
    .filter(c => c.clientX > innerWidth / 2 })
    .take(10)
    .subscribe(c => console.log(c.clientX, c.clientY) })
    Actually kick off computation

    View Slide

  43. First class async
    (Holy sh*t!)

    View Slide

  44. Observable
    Observer
    pattern
    Iterator
    pattern

    View Slide

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

    View Slide

  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

    View Slide

  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));

    View Slide

  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!
    });

    View Slide

  49. View Slide

  50. No external state

    View Slide

  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

    View Slide

  52. filter { }

    View Slide

  53. map { }
    1 2 3 4 5
    1 4 6 8 10

    View Slide

  54. 0 1
    merge
    1
    0 0 1
    A
    B
    C
    3
    2
    0
    1 3
    2
    100ms
    200ms

    View Slide

  55. throttleFirst(300)
    1 2 3
    1 3
    300ms
    4 5 6
    300ms
    7
    7

    View Slide

  56. Flat
    A
    A
    1
    A
    2
    A
    3
    FlatMap

    View Slide

  57. fromArray
    fromCallback
    fromEvent
    fromEventPattern
    fromIterable
    fromNodeCallback
    fromPromise

    View Slide

  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"

    View Slide

  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

    View Slide

  60. View Slide

  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))

    View Slide

  62. Only scratching the surface

    View Slide

  63. Only scratching the surface
    Schedulers
    Async testing

    Libraries

    View Slide

  64. View Slide

  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 =>

    Toggle me
    {toggled ? 'ON' : 'off'}

    )
    };
    }
    let drivers = {
    DOM: makeDOMDriver('#app')
    };
    Cycle.run(main, drivers);

    View Slide

  66. View Slide

  67. https://pragprog.com/book/smreactjs/reactive-programming-with-rxjs

    View Slide

  68. Thanks!
    @sergimansilla

    View Slide