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

E9f890826b3eb5b96a2fea0ffc7a8242?s=128

Duarte Nunes

January 16, 2014
Tweet

More Decks by Duarte Nunes

Other Decks in Programming

Transcript

  1. Reactive Programming with RxJava Duarte Nunes duarte@midokura.com

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

    concurrency
  3. Reactive

  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
  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
  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
  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
  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 P E REACTIVE
  9. 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
  10. Functional

  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
  12. 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
  13. 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
  14. 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
  15. Callbacks GWT AsyncCallback 1 service.getShapes(dbName, new AsyncCallback() { 2 def

    onSuccess(result: Array[Shape]): Unit = { /* ... */ } 3 def onFailure(caught: Throwable): Unit { /* ... */ } 4 });
  16. 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
  17. Observers 1 trait Observer[-A] { 2 def onNext(value: A): Unit

    = {} 3 def onError(error: Throwable): Unit= {} 4 def onCompleted(): Unit = {} 5 }
  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 }
  19. 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
  20. 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 }
  21. Observables 1 trait Observable[+T] { 2 def subscribe(observer: Observer[T]): Subscription

    3 }
  22. 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
  23. Interacting with data Async SYNC Single Multiple A Iterable[A] Observable[A]

    Future[A]
  24. Observables are… produced & consumed with side-effects sequences of data

    in motion! composed functionally synchronous or asynchronous
  25. Concurrency is abstracted def subcribe(observer: Observer[A]): Subscription Calling thread callback

    thread Calling thread
  26. Concurrency is abstracted Calling thread callback threads thread pool def

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

    Observer[A]): Subscription
  28. Concurrency is abstracted Calling thread callback thread nio Event loop

    def subcribe(observer: Observer[A]): Subscription
  29. 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
  30. 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
  31. Interval interval( ) …

  32. Interval interval( ) … Cold Observable

  33. 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 }
  34. Subscriptions life-cycle management unsubscribe unsubscribe unsubscribe subscription boolean Composite refcount

    multiassignment serial
  35. Subjects onXXX onXXX replaysubject behavioursubject caches values remembers last publication

    requires default value asyncsubject caches the last value waits until sequence is over
  36. 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
  37. 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 _)
  38. Take take(3)

  39. 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 })
  40. 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)
  41. Any = true = false any() any()

  42. 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 })
  43. 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)(_ + _)
  44. 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)(_ + _)
  45. FlatMap flatMap { }

  46. 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 }
  47. Anamorphisms a => observable[a] Catamorphisms Bind / flatMap observable[a] =>

    B observable[a] => Observable[b]
  48. Side-effects

  49. Side-effects compose data in the pipeline

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

    state
  51. Side-effects compose data in the pipeline sometimes side-effects are unavoidable

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

    use map to introduce state def doOnNext(onNext: A => Unit): Observable[A]
  53. 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]
  54. 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]
  55. Error Handling

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

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

    } x
  58. Combining Sequences concat

  59. Combining Sequences concat sequential concat repeat startwith

  60. Combining Sequences merge x x

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

  62. 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 }
  63. switch 1 def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] switch

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

  65. time axis is an unknown

  66. time axis is an unknown Time shifted sequences maps data

    on the time plane Buffer Delay Sample Throttle Timeout
  67. 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
  68. Sharing Observable

  69. Sharing publish Observable ConnectableObservable

  70. Sharing Subject publish Observable ConnectableObservable

  71. Sharing Subject publish Observable ConnectableObservable

  72. Sharing Subject publish connect / refCount Observable ConnectableObservable

  73. Sharing Subject publish connect / refCount Observable ConnectableObservable

  74. Sharing Subject publish connect / refCount Observable ConnectableObservable

  75. 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
  76. 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 })
  77. Concurrency 1 object Future { 2 def apply[A](body: =>T)(implicit executor:

    ExecutionContext): Future[A] 3 }
  78. 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 }
  79. 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 }
  80. 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 }
  81. 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
  82. 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
  83. 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
  84. 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
  85. 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
  86. Thank you Questions?