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 re·ac·tive adjective \r ē - ˈ ak-tiv\ ! 1

    of, relating to, or marked by reaction or reactance 2 readily responsive to a stimulus
  2. 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
  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 P E
  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 E
  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 REACTIVE
  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 Asynchronous Scalable Push VAlueS P E REACTIVE
  7. 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
  8. 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
  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 Functions HIGHER-ORDER (MOSTLY) PURe Composable
  10. 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
  11. Callbacks GWT AsyncCallback 1 service.getShapes(dbName, new AsyncCallback() { 2 def

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

    = {} 3 def onError(error: Throwable): Unit= {} 4 def onCompleted(): Unit = {} 5 }
  14. 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 }
  15. 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
  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 } asynchronously stream back results Goal 1 trait Future[+A] { 2 def subscribe(observer: Observer[A]): Unit 3 def tryCancel(): Boolean 4 }
  17. 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
  18. Observables are… produced & consumed with side-effects sequences of data

    in motion! composed functionally synchronous or asynchronous
  19. Concurrency is abstracted Calling thread callback thread nio Event loop

    def subcribe(observer: Observer[A]): Subscription
  20. 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
  21. 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
  22. 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 }
  23. Subjects onXXX onXXX replaysubject behavioursubject caches values remembers last publication

    requires default value asyncsubject caches the last value waits until sequence is over
  24. 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
  25. 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 _)
  26. 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 })
  27. 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)
  28. 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 })
  29. 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)(_ + _)
  30. 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)(_ + _)
  31. 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 }
  32. Side-effects compose data in the pipeline sometimes side-effects are unavoidable

    use map to introduce state def doOnNext(onNext: A => Unit): Observable[A]
  33. 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]
  34. 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]
  35. 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 }
  36. time axis is an unknown Time shifted sequences maps data

    on the time plane Buffer Delay Sample Throttle Timeout
  37. 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
  38. 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
  39. 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 })
  40. 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 }
  41. 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 }
  42. 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 }
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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