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

RxJS Recipes

RxJS Recipes

RxJS is one of my personal favorite technologies. But, the problem with RxJS is the very high learning curve. In this talk, we will take a look at some patterns that can be applied to Angular applications and some more advanced recipes that you should be able to use in your own projects!

Kwinten Pisman

December 06, 2019
Tweet

More Decks by Kwinten Pisman

Other Decks in Programming

Transcript

  1. RxJS Patterns
    Towards Formalising Common RxJS Patterns
    @KwintenP
    Kwinten Pisman

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. const show$ = this.sourceJokes$.pipe(
    skip(1),
    mapTo(true),
    );
    const hide$ = this.update$.pipe(mapTo(false));
    const showNotificationSettings$ = this.settingsService.settings$.pipe(
    select(({ showNotifications }) => showNotifications),
    );
    this.showNotification$ = showNotificationSettings$.pipe(
    switchMap(showNotifications => {
    if (showNotifications) {
    return merge(show$, hide$).pipe(startWith(false));
    }
    return of(false);
    }),
    );

    View Slide

  6. Thank you Dominic

    View Slide

  7. @KwintenP
    Kwinten Pisman
    strongbrew
    https://strongbrew.io
    StackBlitz
    https://stackblitz.com
    Google Developer Expert
    https://bit.ly/2nAqRrZ

    View Slide

  8. https://angular-checklist.io
    @elmd_ @KwintenP

    View Slide

  9. @elmd_ @KwintenP
    Angular Profiler DevTools

    View Slide

  10. Patterns

    View Slide

  11. “In software engineering, a software
    design pattern is a general, reusable
    solution to a commonly occurring
    problem within a given context in
    software design.”

    View Slide

  12. Different Levels of Patterns
    OPERATORS
    PATTERNS
    RECIPES
    @elmd_ @KwintenP

    View Slide

  13. Patterns by Example

    View Slide

  14. World of Jokes
    @elmd_ @KwintenP

    View Slide

  15. @elmd_ @KwintenP
    What we need is a list of jokes that is cached.
    This cache should be updated every 10 seconds.
    If the call fails, we obviously want to retry this
    call every 5 seconds, but only when we are
    online. Makes no sense to retry with no internet
    connection. Now, these jokes should be shown
    on the screen but only update on demand when
    the user requests an update… oh and don't forget
    to notify the user that there is new data available.
    And obviously everything, the polling interval, the
    notifications and polling itself must be
    configurable. In case one of these settings are
    updated, we must restart everything!
    Request from your Manager

    View Slide

  16. @elmd_ @KwintenP

    View Slide

  17. Divide & Conquer

    View Slide

  18. @elmd_ @KwintenP
    Problem
    Sub-Problem A Sub-Problem B
    Solution to Problem
    Solution to
    Sub-Problem B
    Solution to
    Sub-Problem A

    View Slide

  19. Whenever you have a difficult
    situation, just split it up, fix the
    small parts and reassemble
    everything back together to get
    the final solution!
    SCHRUTE’s TIP #1

    View Slide

  20. What we need is a list of jokes that is cached.
    This cache should be updated every 10 seconds.
    If the call fails, we obviously want to retry this
    call every 5 seconds, but only when we are
    online. Makes no sense to retry with no internet
    connection. Now, these jokes should be shown
    on the screen but only update on demand when
    the user requests an update… oh and don't forget
    to notify the user that there is new data available.
    And obviously everything, the polling interval, the
    notifications and polling itself must be
    configurable. In case one of these settings are
    updated, we must restart everything!
    Request from your Manager
    @elmd_ @KwintenP

    View Slide

  21. ”The list of jokes should be updated every 5 seconds.
    Requirement #1
    @elmd_ @KwintenP

    View Slide

  22. Pattern Detection

    View Slide

  23. WHEN
    Trigger
    HOW
    WHAT
    Identifying Patterns
    @elmd_ @KwintenP
    Result Pattern

    View Slide

  24. Triggers and results should
    always be streams. If they are
    not streams, create streams
    yourself.
    SCHRUTE’s TIP #2

    View Slide

  25. The list of jokes should be updated every 5 seconds.
    WHEN every 5 seconds
    WHAT cachedJokes$ that updates every 5 seconds

    Requirement #1
    HOW
    when the trigger fires, send HTTP
    request to fetch the list of jokes
    @elmd_ @KwintenP

    View Slide

  26. @elmd_ @KwintenP
    JokesListComponent
    SettingsDialogComponent
    JokesService
    SettingsService
    Application Architecture

    View Slide

  27. View Slide

  28. Pattern #1
    Work that needs to be executed n times (n > 1)
    const result$ = trigger$.pipe(
    (_ => work$)
    );
    Repeater
    Kwinten - Dominic

    View Slide

  29. Translate the written
    requirement into the WHAT,
    WHEN and HOW!
    SCHRUTE’s TIP #3

    View Slide

  30. ” Requirement #2
    jokes$ that is updated on demand when the trigger fires
    when the user manually requests the data
    extracting the most recent version of the data from the
    cache
    @elmd_ @KwintenP
    WHEN
    HOW
    WHAT
    When new data is available, the user has to manually request
    the latest version of the jokes to be shown on the screen.

    View Slide

  31. View Slide

  32. Enricher
    Pattern #2
    Lazily enrich a stream with data when the trigger fires
    const result$ = trigger$.pipe(
    withLatestFrom(enricherSource$),
    map(([trigger, data]) => ),
    );

    View Slide

  33. showNotifcation$ that hides or shows a notification
    ” Requirement #3
    when an update is available (or none is available)
    create two triggers (show and hide) and merge them
    @elmd_ @KwintenP
    WHEN
    HOW
    WHAT
    If an update is available, the user should see a
    notification to update the list of jokes.

    View Slide

  34. View Slide

  35. Group Aggregator
    Pattern #3
    Whenever the result is an Observable
    const FT$ = falseTrigger$.pipe(mapTo(false));
    const TT$ = trueTrigger$.pipe(mapTo(true));
    const result$ = merge(FT$, TT$);

    View Slide

  36. ” Requirement #4
    settings$ that encapsulates state
    when the user changes one of the settings
    keep and update state when the trigger fires
    @elmd_ @KwintenP
    WHEN
    HOW
    WHAT
    The user should be able to manage settings,
    that can be updated in a dialog.

    View Slide

  37. State should always be
    managed within a stream.
    SCHRUTE’s TIP #4

    View Slide

  38. View Slide

  39. State Manager
    Pattern #4
    State that needs to be reactively managed
    const result$ = trigger$.pipe(
    scan((prevState, updates) => {
    return { …prevState, …updates }
    })
    );

    View Slide

  40. Use the State Manager pattern
    as a minimalistic alternative to
    state management libraries like
    redux.
    SCHRUTE’s TIP #5

    View Slide

  41. It should be possible to toggle the notifications and polling

    Requirement #5
    jokes$ and showNotification$
    conditionally stopping and starting of
    the notifications and polling
    @elmd_ @KwintenP
    WHEN
    HOW
    WHAT
    showNotification or enablePolling changes

    View Slide

  42. View Slide

  43. Work Decider
    Pattern #5
    Work that needs to be stopped and restarted when some trigger fires
    const result$ = trigger$.pipe(
    (condition => {
    if (condition) workA$;

    else workB$;
    })
    );

    View Slide

  44. Use observables as the
    boundaries between services
    and components.
    SCHRUTE’s TIP #6

    View Slide

  45. ”If fetching the list fails we want to retry fetching it.
    If we are offline, we only want to retry if we are back
    online. Otherwise, retry every 1 seconds with max of 5.
    Requirement #6
    @elmd_ @KwintenP

    View Slide

  46. Whenever you have a difficult
    situation, just split it up, fix the
    small parts and reassemble it to
    the solution!
    SCHRUTE’s TIP #1
    REMEMBER

    View Slide

  47. If fetching the list fails we want to retry it.

    Requirement #6.1
    @elmd_ @KwintenP

    View Slide

  48. If we are offline, we only want to retry if we are
    online.Otherwise, retry every 1 second with a max of 5.
    ” Requirement #6.2
    if we are offline, do A (retry when online),
    otherwise do B (retry every second, max 5).
    @elmd_ @KwintenP
    WHEN
    HOW
    WHAT cachedJokes$
    when an error occurs (call to the backend fails)

    View Slide

  49. View Slide

  50. Error Decider
    Recipe #6
    Work that needs to be stopped and restarted when some trigger fires
    const result$ = trigger$.pipe(
    retryWhen(error$ => {
    return error$.pipe(switchMap(_ => {
    if (condition) workA$;

    else workB$;
    }))
    )}
    );
    X

    View Slide

  51. From Pattern to Operator

    View Slide

  52. SCHRUTE’s TIP #7
    When you have a pattern in your
    app that keeps on repeating,
    transform it into an operator!

    View Slide

  53. Key Takeaway’s
    @elmd_ @KwintenP
    Divide & Conquer
    Patterns
    Pattern Detection
    Custom Operators

    View Slide

  54. @elmd_ @KwintenP
    Think Reactive !
    Everything is a Stream

    View Slide

  55. Thank you!
    @KwintenP

    View Slide