Home > Software design >  Is there a proper way to chain coroutines Flow?
Is there a proper way to chain coroutines Flow?

Time:12-18

I am looking for a way to chain several flows in similar way as operations were chained in RxJava. Here is a code of current implementation:

            driverPreferencesFlow
                    .stateIn(lifecycleScope)
                    .transform<DriverPreferences, Response<DriverCredentials>> { it ->
                        Log.d(App.TAG, "Got driver from cache, ask for driver from server")
                        repo.getDriver(it.driver.cell, it.driver.secret)
                    }
                    .onStart {
                    }
                    .onCompletion { e ->
                    }
                    .catch { e ->
                        Log.e(App.TAG, "Something went wrong on loading with driver", e)
                        Response.Error.Exception(e)
                    }
                    .collect { it ->
                        Log.d(App.TAG, "Got driver from server")
                        Log.d(App.TAG, "Collect new driver state ${it.javaClass}")
                        _newDriverState.value = it
                    }

At this implementation, the second operation/call (repo.getDriver()) is called, but never finished. Seems suspended.

What is your way to implement similar task?

CodePudding user response:

Am not sure why you're using transform and not a map or flatMap, but according to the documentation you have to emit the value from within the transform function, so I'd assume it'd look like:

.transform<DriverPreferences, Response<DriverCredentials>> { it ->
    Log.d(App.TAG, "Got driver from cache, ask for driver from server")
    emit(repo.getDriver(it.driver.cell, it.driver.secret))
}

CodePudding user response:

When you want to get emits from one Flow and then create another one based on the elements of the first one, you need to use flatMap operation (flatMapConcat/flatMapMerge), same way as in RxJava. Here is a simple examle with two flows of Integers where you can see usage of both flatMap and map operators:

import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

val a = flowOf(1, 2, 3)
val b = flowOf(3, 4, 5)

val result = a.flatMapConcat { intFromA ->
  b.map { intFromB ->
    intFromA   intFromB
  }
}

runBlocking {
  launch {
    result.collect {
      print(it)
    }
  }
}

And yes, as @romstn correctly pointed out, transform operator requires manual calls to emit function, similarly to Observable.create() in RxJava: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/transform.html

  • Related