https://kotlinlang.org logo
#flow
Title
# flow
v

Václav Škorpil

12/12/2021, 7:44 PM
Hi, as far as i know using suspend in function that returns flow is bad practice and you should use CoroutineContext.() insted. But what to do when it is a member function and you need to call suspend fun inside? This is how i would call it with suspend
Copy code
class KeywordService(val keywordRepo: KeywordRepo) {

    fun getKeywordData(
        keywordId: UUID,
    ): Flow<String> {
        val name = keywordRepo.getKeywordName() // suspend function 
        return keywordRepo.getKeywordData(name) // function that returns flow
    }
}
Currently i am calling it with a flow builder, but i am not sure if this is correct.
Copy code
class KeywordService(val keywordRepo: KeywordRepo) {

    fun getKeywordData(
        keywordId: UUID,
    ): Flow<String> = flow {
        val name = keywordRepo.getKeywordName() // suspend function 
        val dataFlow = keywordRepo.getKeywordData(name) // function that returns flow
        emitAll(dataFlow)
    }
}
This way it feels little bit clunky, is there any better way to do it?
f

Francesc

12/12/2021, 8:44 PM
You could do it like this
Copy code
fun getKeywordData(
            keywordId: UUID,
        ): Flow<String> = flow {
            emit(keywordRepo.getKeywordName())
        }.flatmapLatest { name ->
            keywordRepo.getKeywordData(name)
        }
v

Václav Škorpil

12/12/2021, 8:47 PM
But that would get pretty messy when more suspend fun calls are needed, wouldn't it?
f

Francesc

12/12/2021, 8:48 PM
do you have a more concrete example of your use case?
you can always wrap your suspend functions in an extension function that converts them to a flow and then you could collect those and emit a single item that you flaptmap on
v

Václav Škorpil

12/12/2021, 8:53 PM
Copy code
fun getKeywordData(
        keywordId: UUID,
    ): Flow<String> = flow {
        val name = keywordRepo.getKeywordName() // suspend function 
        val secondArgument = differentRepo.getArgument() // suspend function 
        val thirdArgument = differentRepo.getThirdArgument() // suspend function 
        val dataFlow = keywordRepo.getKeywordData(name, secondArgument, thirdArgument) // function that returns flow
        emitAll(dataFlow)
    }
If you have something like this, then you would need to create a class to wrap them and return them in a flow right?
f

Francesc

12/12/2021, 8:56 PM
yes, but you could either create a suspend function that calls each suspending functiona and returns when it has collected all the data, and then you flatmap on that, or you could convert each to a flow and then use
combine
to collect them all
Copy code
combine(
    flow { emit(suspendOne()) },
    flow { emit(suspendTwo()) },
) { one, two ->
    MyData(one, two)
}.flatmapLatest { data ->
    myFlow(data)
}
the other option is
Copy code
suspend fun aggregator(): MyData {
    val one = getOne()
    val two = getTwo()
    return MyData(one, two)
}
and you could also use
async
to make these 2 run in parallel
Copy code
suspend fun aggregator(): MyData = coroutineScope {
    val one = async { getOne() }
    val two = async { getTwo() }
    return MyData(one.await(), two.await())
}
v

Václav Škorpil

12/12/2021, 9:03 PM
Yes, that solution with combine looks nice, Thank you 🙂 Also could you please, explain me why that first solution that i currently use is wrong?
f

Francesc

12/12/2021, 9:05 PM
it'snot wrong, it's perfectly valid
I think it's a bit less idiomatic, but it's otherwise good
v

Václav Škorpil

12/12/2021, 9:09 PM
I feel like it is the most straight forward, but not sure about performance as i am emiting all items to a new flow. Anyways thank you very much for your time, i am very happy that i know new ways how to work with flows.
👍 1
8 Views