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

Reactive Programming with RxJava

Reactive Programming with RxJava

Scala BCN meet-up @ Midokura

Duarte Nunes

January 16, 2014
Tweet

More Decks by Duarte Nunes

Other Decks in Programming

Transcript

  1. Reactive
    Programming
    with
    RxJava
    Duarte Nunes
    [email protected]

    View full-size slide

  2. Agenda
    Functional reactive
    rx
    fundamental types
    operators
    Taming the Sequence
    concurrency

    View full-size slide

  3. Reactive
    re·ac·tive adjective \r
    ē
    -
    ˈ
    ak-tiv\
    !
    1 of, relating to, or marked by reaction or reactance
    2 readily responsive to a stimulus

    View full-size slide

  4. Reactive
    re·ac·tive adjective \r
    ē
    -
    ˈ
    ak-tiv\
    !
    1 of, relating to, or marked by reaction or reactance
    2 readily responsive to a stimulus
    P

    View full-size slide

  5. Reactive
    re·ac·tive adjective \r
    ē
    -
    ˈ
    ak-tiv\
    !
    1 of, relating to, or marked by reaction or reactance
    2 readily responsive to a stimulus
    P
    E

    View full-size slide

  6. Reactive
    re·ac·tive adjective \r
    ē
    -
    ˈ
    ak-tiv\
    !
    1 of, relating to, or marked by reaction or reactance
    2 readily responsive to a stimulus
    P
    E

    View full-size slide

  7. Reactive
    re·ac·tive adjective \r
    ē
    -
    ˈ
    ak-tiv\
    !
    1 of, relating to, or marked by reaction or reactance
    2 readily responsive to a stimulus
    P
    E
    REACTIVE

    View full-size slide

  8. Reactive
    re·ac·tive adjective \r
    ē
    -
    ˈ
    ak-tiv\
    !
    1 of, relating to, or marked by reaction or reactance
    2 readily responsive to a stimulus
    Asynchronous
    Scalable
    Push
    VAlueS
    P
    E
    REACTIVE

    View full-size slide

  9. Functional
    func·tion·al adjective \
    ˈ
    f
    əŋ
    (k)-shn
    ə
    l, -sh
    ə
    -n
    ə
    l\
    !
    1 of, relating to, or indicating mathematical functions
    2 capable of functioning; working

    View full-size slide

  10. Functional
    func·tion·al adjective \
    ˈ
    f
    əŋ
    (k)-shn
    ə
    l, -sh
    ə
    -n
    ə
    l\
    !
    1 of, relating to, or indicating mathematical functions
    2 capable of functioning; working

    View full-size slide

  11. Functional
    func·tion·al adjective \
    ˈ
    f
    əŋ
    (k)-shn
    ə
    l, -sh
    ə
    -n
    ə
    l\
    !
    1 of, relating to, or indicating mathematical functions
    2 capable of functioning; working
    Functions
    HIGHER-ORDER
    (MOSTLY) PURe
    Composable

    View full-size slide

  12. RxJava
    “a library for composing
    asynchronous and event-based
    programs using observable
    sequences for the Java VM"
    https://github.com/Netflix/RxJava
    A Java port of Rx https://github.com/Reactive-Extensions
    Reactive Extensions

    View full-size slide

  13. Callbacks
    GWT AsyncCallback
    1 service.getShapes(dbName, new AsyncCallback() {
    2 def onSuccess(result: Array[Shape]): Unit = { /* ... */ }
    3 def onFailure(caught: Throwable): Unit { /* ... */ }
    4 });

    View full-size slide

  14. Callbacks
    GWT AsyncCallback
    1 service.getShapes(dbName, new AsyncCallback() {
    2 def onSuccess(result: Array[Shape]): Unit = { /* ... */ }
    3 def onFailure(caught: Throwable): Unit { /* ... */ }
    4 });
    one-shot
    No cancellation
    asynchronous computations are not values

    View full-size slide

  15. Observers
    1 trait Observer[-A] {
    2 def onNext(value: A): Unit = {}
    3 def onError(error: Throwable): Unit= {}
    4 def onCompleted(): Unit = {}
    5 }

    View full-size slide

  16. Futures?
    1 trait Future[+A] {
    2 def onComplete[B](f: Try[A] => B): Unit
    3 def isCompleted: Boolean
    4 def value: Option[Try[A]]
    5 }

    View full-size slide

  17. Futures?
    1 trait Future[+A] {
    2 def onComplete[B](f: Try[A] => B): Unit
    3 def isCompleted: Boolean
    4 def value: Option[Try[A]]
    5 }
    asynchronously stream back results
    Goal

    View full-size slide

  18. Futures?
    1 trait Future[+A] {
    2 def onComplete[B](f: Try[A] => B): Unit
    3 def isCompleted: Boolean
    4 def value: Option[Try[A]]
    5 }
    asynchronously stream back results
    Goal
    1 trait Future[+A] {
    2 def subscribe(observer: Observer[A]): Unit
    3 def tryCancel(): Boolean
    4 }

    View full-size slide

  19. Observables
    1 trait Observable[+T] {
    2 def subscribe(observer: Observer[T]): Subscription
    3 }

    View full-size slide

  20. Duality
    ITERATOR OBSERVER
    def next(): A
    throws Throwable
    def hasNext(): Boolean
    def onNext(a: A): Unit
    def onError(t: Throwable): Unit
    def onCompleted(): Unit
    ITERATABLE OBSERVABLE
    def iterator: Iterator[A] def subscribe(o: Observer[A]): Subscription
    getDataFromMemory() drop(10) take(5) map { _ + "_transformed" } foreach println
    getDataFromNetwork() drop(10) take(5) map { _ + "_transformed" } subscribe println

    View full-size slide

  21. Interacting with data
    Async
    SYNC
    Single Multiple
    A Iterable[A]
    Observable[A]
    Future[A]

    View full-size slide

  22. Observables are…
    produced & consumed
    with side-effects
    sequences of data in motion!
    composed
    functionally
    synchronous or
    asynchronous

    View full-size slide

  23. Concurrency is abstracted
    def subcribe(observer: Observer[A]): Subscription
    Calling thread callback thread
    Calling thread

    View full-size slide

  24. Concurrency is abstracted
    Calling thread callback threads
    thread pool
    def subcribe(observer: Observer[A]): Subscription

    View full-size slide

  25. Concurrency is abstracted
    Calling thread callback thread
    Actor
    def subcribe(observer: Observer[A]): Subscription

    View full-size slide

  26. Concurrency is abstracted
    Calling thread callback thread
    nio
    Event loop
    def subcribe(observer: Observer[A]): Subscription

    View full-size slide

  27. Contract
    OnNext(t)* (OnCompleted() | OnError(e))?
    after zero or more OnNext calls, either one of
    OnCompleted or OnError will optionally be called
    observers can assume to be synchronised
    conceptually they run under a lock,
    similar to the reactor pattern
    Observable.synchronize
    UNsubscribing May not cancel
    Outsanding work work already in progress might still
    complete as it is not always
    safe to abort work in progress, but should
    not be signaled to unsubscribed observers

    View full-size slide

  28. Observable.create
    1 def create(subscribe: Observer[A] => Subscription): Observable[A]
    2
    3 Observable.create(observer => {
    4 try {
    5 observer.onNext(...)
    6 observer.onCompleted();
    7 } catch(Exception e) {
    8 observer.onError(e);
    9 }
    10 })
    x

    View full-size slide

  29. Interval
    interval( )

    View full-size slide

  30. Interval
    interval( )

    Cold Observable

    View full-size slide

  31. 1 def myInterval(d: Duration): Observable[Long] = {
    2 val t = new Timer(true)
    3 Observable.create(observer => {
    4 @volatile var done = false
    5 var c = 0l
    6 def createTimerTask() = new TimerTask() {
    7 def run() {
    8 if (done) return
    9 observer.onNext(c)
    10 c += 1
    11 t.schedule(createTimerTask(), d.toMillis)
    12 }}
    13 t.schedule(createTimerTask(), d.toMillis)
    14 Subscription { done = true; }
    15 })
    16 }

    View full-size slide

  32. Subscriptions
    life-cycle management
    unsubscribe
    unsubscribe
    unsubscribe
    subscription
    boolean
    Composite
    refcount
    multiassignment
    serial

    View full-size slide

  33. Subjects
    onXXX
    onXXX
    replaysubject behavioursubject
    caches values remembers last publication
    requires default value
    asyncsubject
    caches the last value
    waits until sequence is over

    View full-size slide

  34. From future
    1 def from[T](f: Future[T])(implicit ec: ExecutionContext):
    2 Observable[T] = {
    3 val s = AsyncSubject[T]()
    4 f.onComplete {
    5 case Success(c) =>
    6 s.onNext(c)
    7 s.onCompleted()
    8 case Failure(e) =>
    9 s.onError(e)
    10 }
    11 s
    12 }
    x

    View full-size slide

  35. assume
    1 implicit class ObservableOps[A](val o: Observable[A]) extends AnyVal {
    2 // operator definitions go here
    3 }
    4
    5 Observable.interval(200 millis) take(3) filter (isEven _)

    View full-size slide

  36. 1 def myTake(n: Int): Observable[A] =
    2 Observable.create(observer => {
    3 var c = n
    4 var s = CompositeSubscription()
    5 s += o.subscribe(
    6 ((a: A) => {
    7 if (c > 0) {
    8 observer.onNext(a)
    9 c -= 1
    10 if (c == 0) {
    11 s.unsubscribe()
    12 observer.onCompleted()
    13 }
    14 }
    15 }),
    16 observer.onError _,
    17 observer.onCompleted _)
    18 s
    19 })

    View full-size slide

  37. Filter
    x
    x
    1 def myFilter(p: A => Boolean): Observable[A] =
    2 Observable.create(observer => {
    3 o.subscribe(
    4 (a: A) => {
    5 if (p(a))
    6 observer.onNext(a)
    7 },
    8 observer.onError _,
    9 observer.onCompleted _)
    10 })
    filter(isEven)

    View full-size slide

  38. Any
    = true
    = false
    any()
    any()

    View full-size slide

  39. 1 def myAny(): Observable[Boolean] =
    2 Observable.create(observer => {
    3 var s = CompositeSubscription()
    4 var hasReturned = false
    5 s += o.subscribe(
    6 (_: A) => {
    7 if (!hasReturned) {
    8 hasReturned = true
    9 observer.onNext(true)
    10 observer.onCompleted()
    11 s.unsubscribe
    12 }
    13 },
    14 observer.onError _,
    15 () => {
    16 if (!hasReturned) {
    17 observer.onNext(false)
    18 observer.onCompleted()
    19 }
    20 })
    21 s
    22 })

    View full-size slide

  40. FoldLeft
    1 def myFoldLeft[B](z: B)(f: (B, A) => B): Observable[B] =
    2 Observable.create(observer => {
    3 var acc = z
    4 o.subscribe(
    5 (a: A) => acc = f(acc, a),
    6 observer.onError _,
    7 () => {
    8 observer.onNext(acc)
    9 observer.onCompleted()
    10 })
    11 })
    foldLeft(0)(_ + _)

    View full-size slide

  41. FoldLeft
    1 def myFoldLeft[B](z: B)(f: (B, A) => B): Observable[B] =
    2 Observable.create(observer => {
    3 var acc = z
    4 o.subscribe(
    5 (a: A) => acc = f(acc, a),
    6 observer.onError _,
    7 () => {
    8 observer.onNext(acc)
    9 observer.onCompleted()
    10 })
    11 })
    methods like single() force a result
    but are blocking
    Careful
    foldLeft(0)(_ + _)

    View full-size slide

  42. FlatMap
    flatMap { }

    View full-size slide

  43. 1 def flatMap[R](f: T => Observable[R]): Observable[R]
    2
    3
    4 def filter(predicate: A => Boolean): Observable[A] =
    5 o flatMap { x =>
    6 if (predicate(x))
    7 Observable.items(x);
    8 else
    9 Observable.empty[A]();
    10 }
    11
    12 def map[R](func: T => R): Observable[R] = {
    13 o flatMap (func andThen Obsersable.items)
    14 }

    View full-size slide

  44. Anamorphisms
    a => observable[a]
    Catamorphisms
    Bind / flatMap
    observable[a] => B
    observable[a] => Observable[b]

    View full-size slide

  45. Side-effects

    View full-size slide

  46. Side-effects
    compose data in the pipeline

    View full-size slide

  47. Side-effects
    compose data in the pipeline
    use map to introduce state

    View full-size slide

  48. Side-effects
    compose data in the pipeline
    sometimes side-effects are unavoidable
    use map to introduce state

    View full-size slide

  49. Side-effects
    compose data in the pipeline
    sometimes side-effects are unavoidable
    use map to introduce state
    def doOnNext(onNext: A => Unit): Observable[A]

    View full-size slide

  50. Side-effects
    compose data in the pipeline
    sometimes side-effects are unavoidable
    beware of mutable elements
    use map to introduce state
    def doOnNext(onNext: A => Unit): Observable[A]

    View full-size slide

  51. Observable .toBlockingObservable
    Leaving the monad
    1 def foreach(f: A => Unit): Unit
    2
    3 def withFilter(p: A => Boolean): WithFilter[A]
    4
    5 def toIterable: Iterable[A]
    6
    7 def single: A
    8
    9 def singleOption: Option[A]
    10
    11 def toFuture: Future[A]

    View full-size slide

  52. Error Handling

    View full-size slide

  53. Error Handling
    1 def retry(retryCount: Int): Observable[T]
    retry

    View full-size slide

  54. Error Handling
    1 def retry(retryCount: Int): Observable[T]
    retry
    onErrorResumeNext { }
    x

    View full-size slide

  55. Combining Sequences
    concat

    View full-size slide

  56. Combining Sequences
    concat
    sequential concat
    repeat
    startwith

    View full-size slide

  57. Combining Sequences
    merge
    x
    x

    View full-size slide

  58. Combining Sequences
    merge
    concurrent
    amb switch zip merge
    x
    x

    View full-size slide

  59. 1 def myMerge(other: Observable[A]): Observable[A] =
    2 Observable.create(observer => {
    3 val s = CompositeSubscription()
    4 val obs = createObserver(s)
    5 s += o.subscribe(obs)
    6 s += other.subscribe(obs)
    7 s
    8 })
    1 def createObserver(s: Subscription): Observer[A] = {
    2 var done = false
    3 Observer(
    4 (a: A) => {
    5 synchronized
    6 if (!done)
    7 observer.onNext(a)
    8 }, t => {
    9 synchronized
    10 if (!done) {
    11 done = true; s.unsubscribe; observer.onError(t)
    12 }
    13 }, () => {
    14 synchronized
    15 if (!done) {
    16 done = true; s.unsubscribe; observer.onCompleted()
    17 }
    18 })
    19 }

    View full-size slide

  60. switch
    1 def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U]
    switch

    View full-size slide

  61. An example
    switch
    observeChanges(output)
    map(word => completions(word))
    throttle(10 millis)
    textChanges(input)

    View full-size slide

  62. time axis is an unknown

    View full-size slide

  63. time axis is an unknown
    Time shifted
    sequences
    maps data on the
    time plane
    Buffer
    Delay
    Sample
    Throttle
    Timeout

    View full-size slide

  64. time axis is an unknown
    Time shifted
    sequences
    maps data on the
    time plane
    Buffer
    Delay
    Sample
    sequences
    of coincidence
    Throttle
    Timeout
    Buffer
    Join
    Window
    sliding windows

    View full-size slide

  65. Sharing
    Observable

    View full-size slide

  66. Sharing
    publish
    Observable ConnectableObservable

    View full-size slide

  67. Sharing
    Subject
    publish
    Observable ConnectableObservable

    View full-size slide

  68. Sharing
    Subject
    publish
    Observable ConnectableObservable

    View full-size slide

  69. Sharing
    Subject
    publish
    connect / refCount
    Observable ConnectableObservable

    View full-size slide

  70. Sharing
    Subject
    publish
    connect / refCount
    Observable ConnectableObservable

    View full-size slide

  71. Sharing
    Subject
    publish
    connect / refCount
    Observable ConnectableObservable

    View full-size slide

  72. Sharing
    Subject
    publish
    connect / refCount
    1 val o = Observable.interval(200 millis)
    2 val c: ConnectableObservable[Long] = o.publish
    3 c.subscribe(x => println(x))
    4 val s = c.connect
    5 c.subscribe(x => println(x))
    6 s.unsubscribe
    Observable ConnectableObservable

    View full-size slide

  73. from iterable
    infinity
    and beyond?
    1 def myFrom[A](xs: Iterable[A]): Observable[A] =
    2 Observable.create(observer => {
    3 xs foreach (observer.onNext _)
    4 observer.onCompleted()
    5 Subscription { }
    6 })

    View full-size slide

  74. Concurrency
    1 object Future {
    2 def apply[A](body: =>T)(implicit executor: ExecutionContext): Future[A]
    3 }

    View full-size slide

  75. Concurrency
    1 object Future {
    2 def apply[A](body: =>T)(implicit executor: ExecutionContext): Future[A]
    3 }
    1 trait Observable[A] {
    2 def observeOn(scheduler: Scheduler): Observable[A]
    3 }

    View full-size slide

  76. Concurrency
    1 object Future {
    2 def apply[A](body: =>T)(implicit executor: ExecutionContext): Future[A]
    3 }
    1 trait Observable[A] {
    2 def observeOn(scheduler: Scheduler): Observable[A]
    3 }

    View full-size slide

  77. Schedulers
    1 trait Scheduler {
    2 def schedule(action: => Unit): Subscription
    3 def schedule(action: Scheduler => Subscription): Subscription
    4 def scheduleRec(work: (=>Unit) => Unit): Subscription
    5 // ...
    6 }

    View full-size slide

  78. from iterable
    1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] =
    2 Observable.create(observer => {
    3 s schedule {
    4 xs foreach (observer.onNext _)
    5 observer.onCompleted()
    6 }
    7 })
    again

    View full-size slide

  79. from iterable
    1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] =
    2 Observable.create(observer => {
    3 s schedule {
    4 xs foreach (observer.onNext _)
    5 observer.onCompleted()
    6 }
    7 })
    still Wrong
    again

    View full-size slide

  80. from iterable
    again
    1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] =
    2 Observable.create(observer => {
    3 s schedule {
    4 xs foreach (observer.onNext _)
    5 observer.onCompleted()
    6 }
    7 })
    still Wrong
    again
    and

    View full-size slide

  81. from iterable
    again
    1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] =
    2 Observable.create(observer => {
    3 s schedule {
    4 xs foreach (observer.onNext _)
    5 observer.onCompleted()
    6 }
    7 })
    still Wrong
    1 def myFrom3[A](xs: Iterable[A], s: Scheduler): Observable[A] =
    2 Observable.create(observer => {
    3 val it = xs.iterator
    4 s.scheduleRec(self => {
    5 if (it.hasNext) {
    6 observer.onNext(it.next())
    7 self
    8 } else observer.onCompleted()
    9 })})
    again
    and

    View full-size slide

  82. R
    1 implicit class SchedulerOps(val s: Scheduler) extends AnyVal {
    2
    3 def mySchedule(work: (=>Unit) => Unit): Subscription =
    4 s.schedule(scheduler => {
    5
    6 val subscription = MultipleAssignmentSubscription()
    7
    8 def loop(): Unit = {
    9 subscription.subscription = scheduler.schedule {
    10 work { loop() }
    11 }
    12 }
    13
    14 loop()
    15
    16 subscription
    17 })
    18 }
    ecursive
    scheduling

    View full-size slide

  83. Thank
    you
    Questions?

    View full-size slide