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

Combining Observables in Rx

Combining Observables in Rx

Introduction and graphical description to common ReactiveX operators to combine multiple sources of data.

Santiago Vanegas

October 11, 2018
Tweet

Other Decks in Programming

Transcript

  1. INTRODUCTION 2 • Handle more than one source of data

    • Process and perform calculations • Let’s dig into some ReactiveX operators
  2. WHAT IS AN OBSERVABLE? 3 • An Observable emits items

    • An observer subscribes to an Observable. • That observer reacts to whatever item or sequence of items the Observable emits. • It does not block while waiting for the Observable to emit items.
  3. OPERATORS 4 • Concat • Merge • Merge Delay Error

    • Zip • Combine Latest • With Latest From
  4. OPERATORS 5 • Concat • Merge • Merge Delay Error

    • Zip • Combine Latest • With Latest From
  5. OPERATORS 6 • Concat • Merge • Merge Delay Error

    • Zip • Combine Latest • With Latest From
  6. OPERATORS 7 • Concat • Merge • Merge Delay Error

    • Zip • Combine Latest • With Latest From
  7. CONCAT • Once first sequence completes, second one is subscribed

    • Infinite Observable means that will avoid subsequent Observables to be called • Two or more Observables emitting same type 9 1 x 1
  8. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 1, welcome aboard!
  9. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 1, welcome aboard!
  10. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 1, welcome aboard!
  11. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 1, welcome aboard!
  12. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 1, welcome aboard!
  13. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 2, welcome aboard! onComplete()
  14. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 2, welcome aboard! onComplete()
  15. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 3, welcome aboard! onComplete() onComplete()
  16. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 3, welcome aboard! onComplete() onComplete()
  17. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 Group 3, welcome aboard! onComplete() onComplete()
  18. CONCAT 10 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 onComplete() onComplete() onComplete() onComplete()
  19. 11 CONCAT Typical way to fetch data from cache first,

    otherwise from network. val cacheRequest = contactsRepository.getLocalContacts() val networkRequest = contactsRepository.getRemoteContacts() Observable .concat(cacheRequest, networkRequest) .filter { contacts -> contacts.isNotEmpty() } .firstElement() .subscribeBy { contacts -> populateContacts(contacts) }}
  20. 12 CONCAT Typical way to fetch data from cache first,

    otherwise from network. val cacheRequest = contactsRepository.getLocalContacts() val networkRequest = contactsRepository.getRemoteContacts() Observable .concat(cacheRequest, networkRequest) .filter { contacts -> contacts.isNotEmpty() } .firstElement() .subscribeBy { contacts -> populateContacts(contacts) }}
  21. 13 CONCAT Typical way to fetch data from cache first,

    otherwise from network. val cacheRequest = contactsRepository.getLocalContacts() val networkRequest = contactsRepository.getRemoteContacts() Observable .concat(cacheRequest, networkRequest) .filter { contacts -> contacts.isNotEmpty() } .firstElement() .subscribeBy { contacts -> populateContacts(contacts) }}
  22. 14 CONCAT Typical way to fetch data from cache first,

    otherwise from network. val cacheRequest = contactsRepository.getLocalContacts() val networkRequest = contactsRepository.getRemoteContacts() Observable .concat(cacheRequest, networkRequest) .filter { contacts -> contacts.isNotEmpty() } .firstElement() .subscribeBy { contacts -> populateContacts(contacts) }}
  23. 15 CONCAT Typical way to fetch data from cache first,

    otherwise from network. val cacheRequest = contactsRepository.getLocalContacts() val networkRequest = contactsRepository.getRemoteContacts() Observable .concat(cacheRequest, networkRequest) .filter { contacts -> contacts.isNotEmpty() } .firstElement() .subscribeBy { contacts -> populateContacts(contacts) }}
  24. 16 CONCAT Typical way to fetch data from cache first,

    otherwise from network. val cacheRequest = contactsRepository.getLocalContacts() val networkRequest = contactsRepository.getRemoteContacts() Observable .concat(cacheRequest, networkRequest) .filter { contacts -> contacts.isNotEmpty() } .firstElement() .subscribeBy { contacts -> populateContacts(contacts) }}
  25. MERGE Similar to concat, but will combine emissions of two

    or more Observables, and may interleave them. 17
  26. MERGE • All emissions are merged together • Doesn’t take

    into account order or completion • Items emitted may interleave • Error breaks the sequence 18 1 x 1
  27. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard!
  28. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard!
  29. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard!
  30. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard!
  31. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard! onComplete()
  32. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard! onComplete()
  33. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard! onComplete() onComplete()
  34. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard! onComplete() onComplete()
  35. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard! onComplete() onComplete() onComplete()
  36. MERGE 19 GROUP 1 GROUP 2 GROUP 3 Result Group

    1 Group 2 Group 3 All groups, welcome aboard! onComplete() onComplete() onComplete() onComplete()
  37. MERGE DELAY ERROR Changes the behaviour of the original merge

    operator, reserving onError notifications until all of the merged observables complete. 20
  38. MERGE DELAY ERROR • Doesn’t terminate immediately when there is

    an error • Holds off on reporting the error and keep processing all source Observables • More than one Observable can emit an error 21
  39. 22 MERGE DELAY ERROR We set up an error to

    be raised 5 seconds after subscribed val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)}
  40. 22 MERGE DELAY ERROR We set up an error to

    be raised 5 seconds after subscribed val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} And let’s create a couple of Observables that emit every 1 and 2 seconds val source1 = Observable }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)}
  41. val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} val source1 = Observable

    }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)} Observable }.mergeDelayError(source1,}source2,}errorObs)} }.observeOn(AndroidSchedulers.mainThread(), true)} }.subscribe({} }//}Good} }},}{ }//}Bad} }})} 23 MERGE DELAY ERROR
  42. val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} val source1 = Observable

    }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)} Observable }.mergeDelayError(source1,}source2,}errorObs)} }.observeOn(AndroidSchedulers.mainThread(), true)} }.subscribe({} }//}Good} }},}{ }//}Bad} }})} 23 MERGE DELAY ERROR
  43. val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} val source1 = Observable

    }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)} Observable }.mergeDelayError(source1,}source2,}errorObs)} }.observeOn(AndroidSchedulers.mainThread(), true)} }.subscribe({} }//}Good} }},}{ }//}Bad} }})} 24 MERGE DELAY ERROR OUTPUT 0 1 2 3 4 5 6 7 8 9 10 E L A P S E D S E C O N D S
  44. val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} val source1 = Observable

    }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)} Observable }.mergeDelayError(source1,}source2,}errorObs)} }.observeOn(AndroidSchedulers.mainThread(), true)} }.subscribe({} }//}Good} }},}{ }//}Bad} }})} 24 MERGE DELAY ERROR OUTPUT A B A A B A A 0 1 2 3 4 5 6 7 8 9 10 E L A P S E D S E C O N D S A B A B A A A B
  45. val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} val source1 = Observable

    }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)} Observable }.mergeDelayError(source1,}source2,}errorObs)} }.observeOn(AndroidSchedulers.mainThread(), true)} }.subscribe({} }//}Good} }},}{ }//}Bad} }})} 25 MERGE DELAY ERROR OUTPUT A B A A B A A 0 1 2 3 4 5 6 7 8 9 10 E L A P S E D S E C O N D S A B A B A A A B
  46. val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} val source1 = Observable

    }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)} Observable }.mergeDelayError(source1,}source2,}errorObs)} }.observeOn(AndroidSchedulers.mainThread(), true)} }.subscribe({} }//}Good} }},}{ }//}Bad} }})} 25 MERGE DELAY ERROR OUTPUT A B A A B A A 0 1 2 3 4 5 6 7 8 9 10 E L A P S E D S E C O N D S A B A B A A A B • Error didn’t stop execution
  47. val errorObs = Observable }.error<Exception>(Exception("Error!")) }.delay(5,}TimeUnit.SECONDS)} val source1 = Observable

    }.interval(1,}TimeUnit.SECONDS)} }.map}{} }"A"} }}} }.take(10)} val source2}=}Observable }.interval(2,}TimeUnit.SECONDS)} }.map {} }"B"} }}} }.take(5)} Observable }.mergeDelayError(source1,}source2,}errorObs)} }.observeOn(AndroidSchedulers.mainThread(), true)} }.subscribe({} }//}Good} }},}{ }//}Bad} }})} 25 MERGE DELAY ERROR OUTPUT A B A A B A A 0 1 2 3 4 5 6 7 8 9 10 E L A P S E D S E C O N D S A B A B A A A B • Error didn’t stop execution • After source1 and source2 complete,
 an error will be emitted here
  48. ZIP Combines sets of items emitted by two or more

    Observables together via a specified function and emit items based on the results of this function 26
  49. ZIP • Emits as many items as the fewest items

    emitted • Combines emissions of each observable • It “gives up” when will know for sure that can’t zip further emissions 27 1 x 1
  50. 29 ZIP Useful when we need to perform multiple API

    requests and wait for all responses.. val workersRequest = workersRepository fetchAvailable() val jobsRequest = jobsRepository fetchPending() Observables ..zip workersRequest,,jobsRequest)} ..subscribeBy {} executeJob it first,}it second)} }} . . . . ( (
  51. 30 ZIP Useful when we need to perform multiple API

    requests and wait for all responses.. val workersRequest = workersRepository fetchAvailable() val jobsRequest = jobsRepository fetchPending() Observables ..zip workersRequest,,jobsRequest)} ..subscribeBy {} executeJob it first,}it second)} }} . . . . ( (
  52. 31 ZIP Useful when we need to perform multiple API

    requests and wait for all responses.. val workersRequest = workersRepository fetchAvailable() val jobsRequest = jobsRepository fetchPending() Observables ..zip workersRequest,,jobsRequest)} ..subscribeBy {} executeJob worker = it first,}job = it second)} }} . . . . ( (
  53. 32 ZIP Useful when we need to perform multiple API

    requests and wait for all responses.. val workersRequest = workersRepository fetchAvailable() val jobsRequest = jobsRepository fetchPending() Observables ..zip workersRequest,,jobsRequest)} ..subscribeBy {} executeJob worker = it first,}job = it second)} }} . . . . ( (
  54. COMBINE LATEST When an item is emitted by either of

    the Observables, combines the latest item emitted by each Observable via a specified function 33
  55. COMBINE LATEST • Emits whenever any of the sources emit*

    • Combines the most recently emitted items through the function provided • Also smart, will “give up” when will know for sure that can’t combine further emissions 34 1 x 1
  56. 35 COMBINE LATEST Let’s see this simple form example…} val

    emailValid = RxTextView }.textChanges emailEditText)} }.map}{}it isValidEmail()}}} val passwordValid = RxTextView }.textChanges passwordEditText)} }.map}{}it length}>=}8}}} Observables }.combineLatest emailValid,}passwordValid)} }.map}{}it first}&&}it second}}} }.subscribeBy}{}submitButton isEnabled}=}it}}} ( ( ( . . . . .
  57. . val emailValid = RxTextView }.textChanges emailEditText)} }.map}{}it isValidEmail()}}} val

    passwordValid = RxTextView }.textChanges passwordEditText)} }.map}{}it length}>=}8}}} Observables }.combineLatest emailValid,}passwordValid)} }.map}{}it first}&&}it second}}} }.subscribeBy}{}submitButton isEnabled}=}it}}} ( ( ( . . . . . 36 COMBINE LATEST Let’s see this simple form example…}
  58. 37 COMBINE LATEST Let’s see this simple form example…} val

    emailValid = RxTextView }.textChanges emailEditText)} }.map}{}it isValidEmail()}}} val passwordValid = RxTextView }.textChanges passwordEditText)} }.map}{}it length}>=}8}}} Observables }.combineLatest emailValid,}passwordValid)} }.map}{}it first}&&}it second}}} }.subscribeBy}{}shouldEnable}->}submitButton isEnabled}=}shouldEnable}}} ( ( ( . . . . .
  59. 38 COMBINE LATEST Let’s see this simple form example…} val

    emailValid = RxTextView }.textChanges emailEditText)} }.map}{}it isValidEmail()}}} ( . val passwordValid = RxTextView }.textChanges passwordEditText)} }.map}{}it length}>=}8}}} ( . Observables }.combineLatest emailValid,}passwordValid) }.map}{}it first}&&}it second}}} }.subscribeBy}{}submitButton isEnabled}=}it}}} ( . . .
  60. 38 COMBINE LATEST Let’s see this simple form example…} val

    emailValid = RxTextView }.textChanges emailEditText)} }.map}{}it isValidEmail()}}} ( . val passwordValid = RxTextView }.textChanges passwordEditText)} }.map}{}it length}>=}8}}} ( . Observables }.combineLatest emailValid,}passwordValid) }.map}{}it first}&&}it second}}} }.subscribeBy}{}submitButton isEnabled}=}it}}} ( . . .
  61. 39 COMBINE LATEST Let’s see this simple form example…} val

    emailValid = RxTextView }.textChanges emailEditText)} }.map}{}it isValidEmail()}}} ( . val passwordValid = RxTextView }.textChanges passwordEditText)} }.map}{}it length}>=}8}}} ( . Observables }.combineLatest emailValid,}passwordValid) { }. it first}&&}it second} }} }.subscribeBy}{}submitButton isEnabled}=}it}}} ( . . .
  62. 39 COMBINE LATEST Let’s see this simple form example…} val

    emailValid = RxTextView }.textChanges emailEditText)} }.map}{}it isValidEmail()}}} ( . val passwordValid = RxTextView }.textChanges passwordEditText)} }.map}{}it length}>=}8}}} ( . Observables }.combineLatest emailValid,}passwordValid) { }. it first}&&}it second} }} }.subscribeBy}{}submitButton isEnabled}=}it}}} ( . . .
  63. WITH LATEST FROM Similar to combineLatest operator but only emits

    items when the single source Observable emits an item. (Not when any of the Observables do) 40
  64. WITH LATEST FROM 50 A CUSTOMER ARRIVES AND GOES AWAY

    WITH LATEST AVAILABLE PRODUCT FROM THE STORE
  65. WITH LATEST FROM 50 A CUSTOMER ARRIVES AND GOES AWAY

    WITH LATEST AVAILABLE PRODUCT FROM THE STORE
  66. REFERENCES 52 • http://reactivex.io/documentation/operators • http://adamborek.com/combinelatest-withlatestfrom-zip/ both emissions • https://thomasnield.gitbooks.io/rxjavafx-guide/content/

    5.%20Combining%20Observables.html • http://www.introtorx.com/Content/v1.0.10621.0/12_CombiningSequences.html • http://androidahead.com/2018/04/06/rxjava-for-android-100-examples-pack/ • Character images from: https://www.freepik.com/free-photos-vectors/background