Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Coroutines and Rx: A battle towards Asynchronicity

Coroutines and Rx: A battle towards Asynchronicity

Amanjeet Singh

August 31, 2019
Tweet

More Decks by Amanjeet Singh

Other Decks in Programming

Transcript

  1. showsApi.getShowByName("Friends") .flatmap { showsApi.getCharacters(it.id) } .subscribe( { /* do something

    with characters*/ }, { /* handle errors retrieving characters*/} ) -Ben Christensen
  2. I O CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters =

    showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  3. I O 0 sm.item = "Friends" sm.label = 1 CoroutineScope.launch(Dispatchers.IO){

    val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  4. I O 0 sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”,

    cont) Continuation CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  5. I O 0 1 show = sm.result sm.label = 2

    sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”, cont) CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  6. I O 0 1 show = sm.result sm.label = 2

    showsApi.getCharacters(show.id, cont) sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”, cont) Continuation CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  7. I O 0 1 show = sm.result sm.label = 2

    showsApi.getCharacters(show.id, cont) sm.item = "Friends" sm.label = 1 showsApi.getShowsByName(“Friends”, cont) Continuation 2 characters = sm.result CoroutineScope.launch(Dispatchers.IO){ val show = showsApi.getShowByName("Friends") val characters = showsApi.getCharacters(show.id) withContext(Dispatchers.Main){ // do something with characters on UI } }
  8. Error Propagation over 1 2 3 5 N N /

    N - 4 -1/3 -1 -3 Destructive 4
  9. Error Handling • Wrap your response in Result class sealed

    class Result<out T> { data class Success<out T>(val data: T) : Result<T>() data class Error(val exception: Exception) : Result<Nothing>() } • Gotta catch them all onError onErrorReturn onErrorResumeNext doOnError onErrorComplete onErrorReturnItem
  10. Error Handling fun getMovies(): Observable<Result<Response>> { return moviesApi.getMovies() .map {

    Result.Success(it) } .onErrorReturn { Result.Error(it) } } Result
  11. Error Handling over • Depends on what Coroutine Builder you

    are using: Launch/Async? • Scope Intelligently • Tools for handling ☠ CoroutineExceptionHandler try/catch
  12. Error Handling over private val context: CoroutineContext = Job() +

    Dispatchers.IO val scope = CoroutineScope(context) suspend fun longRunningTask(): Unit { delay(100) throw IllegalArgumentException() } • Setup
  13. Error Handling over • Install CoroutineExceptionHandler private val ceh =

    CoroutineExceptionHandler { _, e -> println("CEH Handled Crash [$e]") } scope.launch(ceh){ launch{ longRunningTask() } }
  14. suspend fun <T : Any> apiCall(call: suspend () -> Result<T>,

    errorMessage: String): Result<T> { return try { call() } catch (e: Exception) { Result.Error(e) } } Let’s be a pro
  15. suspend fun <T : Any> apiCall(call: suspend () -> Result<T>,

    errorMessage: String): Result<T> { return try { call() } catch (e: Exception) { Result.Error(e) } } Result Let’s be a pro
  16. Let’s be a pro suspend fun getLatestMovies(): Result<MovieResults> { return

    apiCall( { val data = moviesApiInterface.getInTheatreMoviesAsync().await() return@apiCall Result.Success(data.body()) }, "Couldn't get" ) } So, Now are we pro?
  17. suspend fun getLatestMovies(): Result<MovieResults> { return apiCall( { val data

    = moviesApiInterface.getInTheatreMoviesAsync().await() return@apiCall Result.Success(data.body()) }, "Couldn't get" ) } So, Now are we pro? No Let’s be a pro
  18. Zip Calls in Observable.zip(getPopularMovies(), getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies,

    topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  19. Zip Calls in Let’s apply TEST rule Observable.zip(getPopularMovies(), getNowPlayingMovies(), BiFunction<Movies,

    Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  20. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  21. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  22. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  23. Zip Calls in Threading Exception Handling Scalability Transforming results Observable.zip(getPopularMovies(),

    getNowPlayingMovies(), BiFunction<Movies, Movies, Movies> { popularMovies, topRatedMovies, -> // Filter from popular and top rated movies }.subscribeOn(Scheduler.io())
  24. Zip Calls in suspend fun <T1, T2, R> zip(source1: Deferred<T1>,

    source2: Deferred<T2>, zipper: (T1, T2) -> R) = coroutineScope { // wait for two calls to complete async { zipper(source1.await(), source2.await()) } } • Let’s cook zip *
  25. Zip Calls in suspend fun <T1, T2, R> zip(source1: Deferred<T1>,

    source2: Deferred<T2>, zipper: (T1, T2) -> R) = coroutineScope { // wait for two calls to complete async { zipper(source1.await(), source2.await()) } } • Let’s cook zip * Zip Deferred 1 Deferred 2 Zipper Result
  26. Zip Calls in suspend fun <T1, T2, R> zip(source1: Deferred<T1>,

    source2: Deferred<T2>, zipper: (T1, T2) -> R) = coroutineScope { // wait for two calls to complete async { zipper(source1.await(), source2.await()) } } • Let’s cook zip *
  27. Zip Calls in coroutineScope.launch { val popularMovies = async{ getPopularMovies()

    } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() } Threading Exception Handling Scalability Transforming results
  28. Zip Calls in Threading Exception Handling Scalability Transforming results coroutineScope.launch

    { val popularMovies = async{ getPopularMovies() } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() }
  29. Zip Calls in Threading Exception Handling Scalability Transforming results coroutineScope.launch

    { val popularMovies = async{ getPopularMovies() } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() }
  30. Zip Calls in Threading Exception Handling Scalability Transforming results coroutineScope.launch

    { val popularMovies = async{ getPopularMovies() } val topRatedMovies = async{ getTopRatedMovies() } zip(popularMovies, topRatedMovies) { popularResults, topRatedResults -> // Filter from popular and top rated movies return filteredResults }.await() }
  31. State Management through Rich with operators, Adheres to R&FP, exceptional

    handling, 
 lifecycle management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
  32. State Management through Rich with operators, Adheres to R&FP, exceptional

    handling, 
 lifecycle management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
  33. State Management through Rich with operators, Adheres to R&FP, exceptional

    handling, 
 lifecycle management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
  34. State Management through Rich with operators, Adheres to R&FP, exceptional

    handling, 
 lifecycle management, etc. val models = events.flatMap { moviesClient.searchMovies(it.searchTerm) } .map { it.results.asResult() } .onErrorReturn { Result.Error(it.toApplicationError()) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
  35. State Management through • Rich with operators, Adheres to R&FP,

    exceptional handling, 
 lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } }
  36. State Management through • Rich with operators, Adheres to R&FP,

    exceptional handling, 
 lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } }
  37. State Management through • Rich with operators, Adheres to R&FP,

    exceptional handling, 
 lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } }
  38. State Management through • Rich with operators, Adheres to R&FP,

    exceptional handling, 
 lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } }
  39. State Management through • Rich with operators, Adheres to R&FP,

    exceptional handling, 
 lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } }
  40. State Management through • Rich with operators, Adheres to R&FP,

    exceptional handling, 
 lifecycle management, etc. viewModelScope.launch { flowOf(moviesClient.searchMoviesAsync(query).await()) .map { Result.Success(it.results) } .catch { Result.Error(it.toApplicationError()) }.collect { searchStates.postValue(it) } } UI Bindings?
  41. References • https://medium.com/the-kotlin-chronicle/coroutine-exceptions-3378f51a7d33 • https://medium.com/@elizarov/ • https://speakerdeck.com/krlrozov/kotlin-coroutines-flow-is-coming • https://speakerdeck.com/ragdroid/flowing-things-not-so-strange-in-the-mvi-world •

    https://github.com/amanjeetsingh150/CoroutinesRx • https://medium.com/capital-one-tech/coroutines-and-rxjava-an-asynchronicity- comparison-part-1-asynchronous-programming-e726a925342a • Kotlin Coroutines Slack Channel • https://github.com/android/plaid