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

Introduction to Functional-Reactive Programming v2

Introduction to Functional-Reactive Programming v2

v2.0

52982aebec177b0d04ff3d1c3d78bc85?s=128

Grzegorz Dyrda

September 26, 2018
Tweet

More Decks by Grzegorz Dyrda

Other Decks in Programming

Transcript

  1. Grzegorz Dyrda Geeky Devs Studio Introduction to Functional-Reactive Programming

  2. Functional-Reactive Programming

  3. Functional-Reactive Programming Why?

  4. Why? • Java 9 + Reactive Streams (JEP 266) •

    Spring WebFlux, Eclipse Vert.x • Angular, Cycle.js, React (Native) • Android Architecture Components, Room, Retrofit… Airbnb MvRx
  5. Functional-Reactive

  6. Functional + Reactive

  7. Functional Part 1:

  8. You cannot understand Functional-Reactive programming, if you don’t understand Functional

    programming.
  9. What is Functional Programming?

  10. Functional Programming Programming style in which we avoid sharing state

    and data mutation, in favor of composing functions and passing data from one to another.
  11. Functional Programming …is about moving from
 the imperative
 to declarative

    style.
  12. The world’s most popular functional language is… Excel

  13. =IF(A6="", IF(C5<>"", C5, IFERROR(MATCH("?*", A6:$A$20, 0)-1, ROWS(A6:$A$20))&" in "&VLOOKUP(A5,$H$6:$I$8, 2,

    0)), "") Excel =IF(MONTH($C$3)<>MONTH($C$3-(WEEKDAY($C$3;1)-($A$1-1))- IF((WEEKDAY($C$3;1)-($A$1-1))<=0;7;0)+(ROW(F7)-J3- ROW($D$6))*7+(COLUMN(F7)-COLUMN($D$6)+1));"";$C$3- (WEEKDAY($C$3;1)-($A$1-1))-IF((WEEKDAY($C$3;1)- ($A$1-1))<=0;7;0)+(ROW(F7)-J3-ROW($D$6))*7+(COLUMN(F7)- COLUMN($D$6)+1))
  14. Language Support

  15. • Functions as first-class citizens • Higher-order functions • Lambda

    expressions • Immutable data types • Functions are closures Language Support JAVA 8+ APPROVED
  16. Example

  17. Client says: “Make a sum of all our prices
 that

    are higher than $20,
 discounted by 10%” Example
  18. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example
  19. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example var sum = 0.0 for (var i = 0; i <= prices.size; i++) {
 if (price[i] > 20) {
 sum += price[i] * 0.9
 }}
 }} val prices = listOf(20, 12, 15, 21, 25, 28, 30)
  20. var sum = 0.0 for (var i = 0; i

    < prices.size; i++) {
 if (price[i] > 20) {
 sum += price[i] * 0.9
 }}
 }} Example val prices = listOf(20, 12, 15, 21, 25, 28, 30)
  21. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example var sum = 0.0 for (price in prices) {
 if (price > 20) {
 sum += price * 0.9
 }}
 }}
  22. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum() var sum = 0.0 for (price in prices) {
 if (price > 20) {
 sum += price * 0.9
 }}
 }}
  23. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  24. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  25. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  26. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  27. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  28. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum() Important: 1. No import = stdlib 2. Cross-platform
  29. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  30. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum() // Returns list containing only elements matching [predicate] fun <T> filter(predicate: (T) -> Boolean): List<T>
  31. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum() // Returns list containing only elements matching [predicate] fun <T> filter(predicate: (T) -> Boolean): List<T>
  32. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example // Returns list containing elements transformed by [transform] fun <T, R> map(transform: (T) -> R): List<R> val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  33. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example // Returns list containing elements transformed by [transform] fun <T, R> map(transform: (T) -> R): List<R> val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum()
  34. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum() Single, consistent, higher-level API. More readable & closer to the Business Intent
  35. val prices = listOf(20, 12, 15, 21, 25, 28, 30)

    Example val sum = prices
 .filter { price -> price > 20 }
 .map { price -> price * 0.9 }
 .sum() var sum = 0.0 for (price in prices) {
 if (price > 20) {
 sum += price * 0.9
 }}
 }}
  36. Another Example

  37. You’re a Blog admin: “Calculate average blogpost length of all

    your blog’s Users that speak Polish” Example
  38. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example
  39. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val users = db.getAllUsers()
  40. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = ??? val users = db.getAllUsers()
  41. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users val users = db.getAllUsers()
  42. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { ??? }} val users = db.getAllUsers()
  43. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }} val users = db.getAllUsers()
  44. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .map { ??? }} val users = db.getAllUsers()
  45. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .map { user -> user.posts }} val users = db.getAllUsers()
  46. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .map { user -> user.posts } // Return type is: List<List<Post>> // But we need: List<Post> val users = db.getAllUsers()
  47. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .map { user -> user.posts } val users = db.getAllUsers()
  48. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts } val users = db.getAllUsers()
  49. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts } // Returns a single list with all elements yielded from // [transform] invoked on on each element of original collection. fun <T, R> flatMap(transform: (T) -> Iterable<R>): List<R> val users = db.getAllUsers()
  50. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts } // Returns a single list with all elements yielded from // [transform] invoked on on each element of original collection. fun <T, R> flatMap(transform: (T) -> Iterable<R>): List<R> val users = db.getAllUsers()
  51. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts } // Returns a single list with all elements yielded from // [transform] invoked on on each element of original collection. fun <T, R> flatMap(transform: (T) -> Iterable<R>): List<R> val users = db.getAllUsers()
  52. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts } val users = db.getAllUsers()
  53. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts }
 .map { ??? }} val users = db.getAllUsers()
  54. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }} val users = db.getAllUsers()
  55. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .average() val users = db.getAllUsers()
  56. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .average() val users = db.getAllUsers() // Easy to maintain and modify
  57. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .filter { user -> user.language == "PL" }
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .average() val users = db.getAllUsers()
  58. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .take(100)
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .average() val users = db.getAllUsers()
  59. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .take(100)
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .average() val users = db.getAllUsers()
  60. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .take(100)
 .flatMap { user -> user.posts } .map { post -> post.body.length } .count() val users = db.getAllUsers()
  61. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .take(100)
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .reduce { acc, length -> acc + length } val users = db.getAllUsers()
  62. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .take(100)
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .reduce { acc, length -> acc + length }} val users = db.getAllUsers() // Returns a single value computed by accumulating values // returned from applying [operation] to each element. fun <S, T:S> reduce(operation: (acc: S, T) -> S): S
  63. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .take(100)
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .reduce { acc, length -> acc + length }} val users = db.getAllUsers() // Returns a single value computed by accumulating values // returned from applying [operation] to each element. fun <S, T:S> reduce(operation: (acc: S, T) -> S): S
  64. data class Post(val title: String, val body: String)
 data class

    User(val language: String, val posts: List<Post>) Example val avgPolishPostLength = users
 .take(100)
 .flatMap { user -> user.posts }
 .map { post -> post.body.length }
 .fold(0) { acc, _ -> acc + 1 }} val users = db.getAllUsers() // Accumulates value starting with [initial] and applying // [operation] to current accumulator value and each element. fun <T, R> fold(initial: R, operation: (acc: R, T) -> R): R
  65. Benefits

  66. • No explicit mutation, reassignment, iteration etc. = less error

    prone • Low-level details controlled by the library = may be optimized under the hood • More expressive, more readable • Easy to enhance & modify the logic • Closer to the business intent Benefits
  67. Functional -vs- Object-Oriented

  68. Functional -vs- Object-Oriented

  69. Functional + Object-Oriented

  70. // Returns list containing only elements matching [predicate] fun <T>

    filter(predicate: (T) -> Boolean): List<T> // Returns list containing elements transformed by [transform] fun <T, R> map(transform: (T) -> R): List<R> // Flattens nested lists into single list fun <T, R> flatMap(transform: (T) -> Iterable<R>): List<R> // Accumulates value by applying [operation] fun <S, T:S> reduce(operation: (acc: S, T) -> S): S // Accumulates value, starting with [initial] fun <T, R> fold(initial: R, operation: (acc: R, T) -> R): R Common Specialized Functions
  71. None
  72. None
  73. Functional…

  74. Functional + Reactive

  75. Reactive Part 2:

  76. What does is mean to be “Reactive”?

  77. var a = 1 var b = 2 var c

    = a + b print("c = $c") What does is mean to be “Reactive”? // ???
  78. var a = 1 var b = 2 var c

    = a + b print("c = $c") What does is mean to be “Reactive”? // c = 3
  79. var a = 1 var b = 2 var c

    = a + b a = 2 print("c = $c") What does is mean to be “Reactive”? // ???
  80. var a = 1 var b = 2 var c

    = a + b a = 2 print("c = $c") What does is mean to be “Reactive”? // c = 3
  81. The world’s most popular reactive system is… Excel

  82. Excel

  83. What is Reactive Programming?

  84. Reactive Programming Programming with
 Asynchronous Event Streams

  85. Reactive Programming Programming with
 Asynchronous Event Streams

  86. Observer Pattern on Steroids Reactive Programming

  87. Observer Pattern on Functional Steroids Reactive Programming

  88. 1 5 12 3 7 4 This can be anything:

    Number, Object, Blog Post etc.
  89. 1 filter(x > 4) 5 12 3 7 4

  90. 1 filter(x > 4) 5 12 7 5 12 3

    7 4
  91. 1 filter(x > 4) 5 12 7 map(x + 2)

    5 12 3 7 4
  92. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4
  93. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4 So far everything was…
 Synchronous
  94. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4 Introducing…
 Time
  95. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4
  96. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4
  97. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4
  98. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4
  99. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4
  100. 1 filter(x > 4) 5 12 7 map(x + 2)

    7 14 9 5 12 3 7 4
  101. 5 12 7 map(x + 2) (Asynchronous Event) Stream Events

    Operator
  102. 5 12 7 map(x + 2) Stream Events Infinite Operator

  103. 5 12 7 map(x + 2) Stream Events Error Operator

  104. 5 12 7 map(x + 2) Stream Events Complete Operator

  105. Implementation

  106. Akka Reactor Reactive Extensions Vert.x xstream Implementation

  107. Akka Reactor Reactive Extensions Vert.x xstream Implementation

  108. Rx Implementation

  109. • RxJava • RxJS • Rx.NET • RxScala • RxClojure

    • RxCpp Implementation • RxPython • RxGo • RxKotlin • RxSwift • RxPHP • RxDart
  110. Stream

  111. Stream Observable<T>

  112. Stream Observable<T> List<T> -vs-

  113. Creating a Stream

  114. Observable.just(1) Observable.just(true) Observable.just(user) Observable.just("one", "two", "three") Creating a Stream Observable.fromIterable(users)

    Observable.fromArray(usersArray) Observable.fromCallable(this::sendRequest) Observable.fromFuture(future) Observable.defer(...) Observable.create(...)
  115. Observing a Stream

  116. Observable.just(1)) Observing a Stream .subscribe { item ->
 print("Item =

    $item")
 }} // Item = 1
  117. Observing a Stream // Subscribes the given Observer to this

    Observable instance. fun <T> subscribe(observer: Observer<T>): Unit interface Observer<T> {
 fun onNext(t: T)
 fun onError(e: Throwable)
 fun onComplete() ...
 }} // Subscribes to an ObservableSource and provides callbacks. fun <T> subscribe(onNext: (T) -> Unit, onError: (Throwable) -> Unit, onComplete: () -> Unit): Unit
  118. Observable.just(1)) Observing a Stream .subscribe { item ->
 print("Item =

    $item")
 }} // Item = 1
  119. Observable.just(true)) .subscribe { item ->
 print("Item = $item")
 }} //

    Item = true Observing a Stream
  120. Observable.just(user)) .subscribe { item ->
 print("Item = $item")
 }} //

    Item = User(language="PL",posts=...) Observing a Stream
  121. Observable.just("one", "two", "three") .subscribe { item ->
 print("Item = $item")


    }} // Item = one // Item = two // Item = three Observing a Stream
  122. Observable.fromIterable(users)) .subscribe { item ->
 print("Item = $item")
 }} //

    Item = User(language="PL",posts=...) // Item = User(language="EN",posts=...) // Item = User(language="PL",posts=...) // Item = User(language="PL",posts=...) ... Observing a Stream
  123. Observable.fromIterable(users)) .subscribe { item ->
 print("Item = $item")
 }} //

    Item = User(language="PL",posts=...) // Item = User(language="EN",posts=...) // Item = User(language="PL",posts=...) // Item = User(language="PL",posts=...) ... Where are my Steroids? Observing a Stream
  124. Transforming a Stream

  125. Observable.just("5", "8", "3", "7") .subscribe { item ->
 print("Item =

    $item")
 }} Transforming a Stream
  126. Observable.just("5", "8", "3", "7") .map(String::toInt) .subscribe { item ->
 print("Item

    = $item")
 }} Transforming a Stream
  127. Observable.just("5", "8", "3", "7") .map(String::toInt) .filter { item -> item

    > 5 } .subscribe { item ->
 print("Item = $item")
 }} // Item = 8 // Item = 7 Transforming a Stream
  128. None
  129. “There’s an App for that” Apple, 2009

  130. “There’s an Operator for that” Reactive Extensions, 2018

  131. ReactiveX Operators Creating: • create • defer • empty •

    error • fromArray • fromCallable • fromFuture • interval • just • … Transform/Filter: • buffer • flatMap • groupBy • map • scan • window • debounce • distinct • filter • … Combining: • combineLatest • withLatestFrom • merge • switch • zip Threading: • subscribeOn • observeOn • …
  132. Introducing Time

  133. Observable.interval(1, TimeUnit.SECONDS) Interval .subscribe { item ->
 print("Item = $item")


    }} //00:00: Item = 0 //00:01: Item = 1 //00:02: Item = 2 //00:03: Item = 3 //00:04: Item = 4 //...
  134. Observable.interval(1, TimeUnit.SECONDS) Interval .map { item -> item * item

    } .subscribe { item ->
 print("Item = $item")
 }}
  135. Observable.interval(1, TimeUnit.SECONDS) Interval .map { item -> item * item

    }. .take(3) .subscribe { item ->
 print("Item = $item")
 }} //00:00: Item = 0 //00:01: Item = 1 //00:02: Item = 4
  136. // RxBinding RxView.clicks(button) // Observable<Object> .map { click -> 1

    } .scan { acc, next -> acc + next } UI Events // Similar to reduce(), but accumulates values over time, // each time passing the result of [operation] downstream. fun <T> scan(operation: (acc: T, T) -> T): Observable<T>
  137. // RxBinding RxView.clicks(button) // Observable<Object> .map { click -> 1

    } .scan { acc, next -> acc + next } UI Events .subscribe { count ->
 print("Count = $count")
 } // Count = 1 // Count = 2 // Count = 3 // Count = 4 // ...
  138. // Retrofit myService.getUsers() // Observable<User> .map { user -> user.posts.size

    } .reduce { acc, next -> acc + next } Network Requests .subscribe { count ->
 print("Post count = $count")
 } // Post count = 120
  139. Threading

  140. fun fetchUser(): User { /* fetch from network */ }

    Threading Observable.fromCallable(this::fetchUser) .subscribe { user ->
 print("User = $user")
 }} // Error: Network on the Main Thread!
  141. fun fetchUser(): User { /* fetch from network */ }

    Threading Observable.fromCallable(this::fetchUser) .subscribe { user ->
 print("User = $user")
 }} .subscribeOn(Schedulers.io())
  142. fun fetchUser(): User { /* fetch from network */ }

    Threading Observable.fromCallable(this::fetchUser) .subscribe { user ->
 print("User = $user")
 }} .subscribeOn(Schedulers.io())
  143. fun fetchUser(): User { /* fetch from network */ }

    Threading Observable.fromCallable(this::fetchUser) .subscribe { user ->
 print("User = $user")
 }} .subscribeOn(Schedulers.io())
  144. fun fetchUser(): User { /* fetch from network */ }

    Threading Observable.fromCallable(this::fetchUser) .subscribeOn(Schedulers.io()) .subscribe { user -> statusLabel.text = user.firstName
 }} // Error: Accessing UI from the background thread!
  145. fun fetchUser(): User { /* fetch from network */ }

    Threading .subscribe { user -> statusLabel.text = user.firstName
 }} Observable.fromCallable(this::fetchUser) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
  146. fun fetchUser(): User { /* fetch from network */ }

    Threading .subscribe { user -> statusLabel.text = user.firstName
 }} Observable.fromCallable(this::fetchUser) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
  147. fun fetchUser(): User { /* fetch from network */ }

    Threading .subscribe { user -> statusLabel.text = user.firstName
 }} Observable.fromCallable(this::fetchUser) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) // OK
  148. Combining Streams

  149. // Task: perform network request after each click Combining Streams

    // Return type: Observable<Observable<User>> // But we want: Observable<User> val buttonClicks = RxView.clicks(button) val userInfoRequest = myService.getUserInfo() buttonClicks .map { click -> myService.getUserInfo() }
  150. // Returns Observable that emits all items emitted by //

    Observables returned by [mapper]. fun <T, R> flatMap(mapper: (T) -> Observable<R>): Observable<R> buttonClicks .flatMap { click -> myService.getUserInfo() } val buttonClicks = RxView.clicks(button) val userInfoRequest = myService.getUserInfo() // Task: perform network request after each click Combining Streams
  151. .map { user -> user.firstName } .subscribe { firstName ->


    print("firstName = $firstName")
 } // firstName = John val buttonClicks = RxView.clicks(button) val userInfoRequest = myService.getUserInfo() buttonClicks .flatMap { click -> myService.getUserInfo() } // Task: perform network request after each click Combining Streams
  152. // Task: do the same thing on btn1 & btn2

    click val btn1Clicks = RxView.clicks(button1) val btn2Clicks = RxView.clicks(button2) btn1Clicks .map { click -> "Click!" } .subscribe { msg ->
 print("msg = $msg")
 }} Combining Streams
  153. // Task: do the same thing on btn1 & btn2

    click val btn1Clicks = RxView.clicks(button1) val btn2Clicks = RxView.clicks(button2) Observable.merge(btn1Clicks, btn2Clicks) .map { click -> "Click!" } .subscribe { msg ->
 print("msg = $msg")
 }} // Merges two (or more) Observables into a single one. fun <T> merge(s1: Observable<T>, s2: Observable<T>): Observable<T> Combining Streams
  154. Combining Streams

  155. Combining Streams

  156. Netflix

  157. Netflix

  158. Netflix

  159. Netflix

  160. Netflix

  161. Netflix

  162. Netflix

  163. Netflix

  164. Conclusions

  165. • FRP is great for composing/ orchestrating multiple Event streams

    • “There’s an Operator for that” • Declarative Threading, Error handling, Cancellation of async tasks etc. • Do not overuse for simple tasks Conclusions
  166. How to Start?

  167. How to Start? • ReactiveX.io • github.com/ReactiveX • rxmarbles.com •

    geekydevs.com/rx
  168. Thank You

  169. Thank You Any questions?