kotlin-coroutines

How to spawn new coroutines for the processing of each element in a cold flow?


Came across an example that simulates accessing a slow database to obtain a flow of user identifiers which is associated with a profile that's accessible via an even slower network resource:

fun getAllUserIds():Flow<Int>{
    return flow{
        repeat(3){
            delay(200.milliseconds)
            log("Emitting!")
            emit(it)
            }
         } 
      }

suspend fun getProfileFromNetwork(id:Int):String{
    delay(2.seconds)
    return "Profile[$id]"
  }

fun main(){
    val ids = getAllUserIds()
    runBlocking{
        ids
            .map{getProfileFromNetwork(it)}
            .collect{log("Got $it")}
        }
  }

emission of the IDs and the request of profiles are intertwined. How would I optimize this by spawning new coroutines for the processing of each element in the flow?


Solution

  • If you want to get the result as a whole, you can use flatMapMerge, which is designed for this exact scenario: concurrently mapping each element in the flow to a new flow and merging the results.

    fun getAllUserIds(): Flow<Int> {
        return flow {
            repeat(3) {
                delay(200.milliseconds)
                log("Emitting!")
                emit(it)
            }
        }
    }
    
    suspend fun getProfileFromNetwork(id: Int): String {
        delay(2.seconds)
        return "Profile[$id]"
    }
    
    fun main() = runBlocking {
        getAllUserIds()
            .flatMapMerge { id ->
                flow {
                    emit(getProfileFromNetwork(id))
                }
            }
            .collect { log("Got $it") }
    }
    

    But I think of a better approach; show as much as data as fetched to the user and having a visual effect, indicating that some chunk of data is yet to load using for example ShimmerEffect or any other alternative. Try to load it lazily for each item of the list.

    Try to fire an independent coroutine to fetch the Profile when the UserID has been fetched from the database inside the RecyclerView/List item logic (don't forget to have a caching policy to minimize network load).