I have a function doing something like this ```fun...
# coroutines
j
I have a function doing something like this
Copy code
fun initialize() {
    coroutineScope.launch {
        <http://log.info|log.info>(TAG, "Start flow collection")
        startFlowCollection(vim.value)
        <http://log.info|log.info>(TAG, "Done with initialization")
    }
}

suspend fun startFlowCollection() = supervisorScope {
    launch { collectFromCallbackFlow1() }
    launch { collectFromCallbackFlow2() } 
}
But I never get “Done with initialization”. I understand callback flows never finish so I collect them inside
launch
to not suspend forever. But it looks like it is suspending forever so I don’t know what I’m doing wrong here.
j
Is this the actual code? What is the
CoroutineScope
for the 2
launch
-es in
startFlowCollection
? This shouldn't compile as-is, unless
startFlowCollection
is a method in a class that implements
CoroutineScope
itself (which is unlikely)
j
coroutineScope
comes from
CoroutineScope(SupervisorJob() + <http://Dispatchers.IO|Dispatchers.IO>)
. I tried with
suspend fun initialize
but it didn’t work neither, I though it might be due to the scope being cancelled (the code is part from an android app and initialize was called from a lifecycle scope) so I gave the class containing
initialize
its own coroutine scope
j
I meant the
launch
calls inside
startFlowCollection
j
ah, I see, the snippet is missing some code, sorry
I use a
supervisorScope
there
j
Ok now it compiles and makes sense. It's a very important detail. Using
supervisorScope { .. }
here gives a boundary to the lifetime of the 2 launch calls.
startFlowCollection
will only return once the 2 launched coroutines are complete, which never happens if
collectFromCallbackFlowN()
never completes
j
aaaah, indeed it’s an important details hehe
j
Let's assume you call those 2
launch
directly inside
initialize
. They would be part of the coroutine scope of the parent
launch
call. It has the same property: it will not complete until the child coroutines are done. So you would get your "Done with initialization" log, but the launched coroutine would still be running. It might be what you want. I don't know. If your definition of "initialization is done" is "the collecting coroutines are launched" (not done!) then it's fine. But then another way to fix it is to just put the log outside the
launch
in
initialize
j
Yes, I just want to start the collection of the two flows, that’s what “done” meant here. Thanks for the help!
u
No need for
startFlowCollection
to be suspend fun. Make it an extension fun on CoroutineScope and then just call launch (as in this.launch). Then you’ll not be waiting for the new coroutines
j
Given how things look (if it's that simple), I'd actually advocate for:
Copy code
fun initialize() {
    <http://log.info|log.info>(TAG, "Start flow collection")

    coroutineScope.launch { collectFromCallbackFlow1() }
    coroutineScope.launch { collectFromCallbackFlow2() } 

    <http://log.info|log.info>(TAG, "Done with initialization")
}
because there doesn't seem to be a need for an outer launch
u
If this is just for demonstration of the unerlying concepts, or you need the original semantics this. should work:
Copy code
fun initialize() {
    coroutineScope.launch {
        <http://log.info|log.info>(TAG, "Start flow collection")
        startFlowCollection(vim.value)
        <http://log.info|log.info>(TAG, "Done with initialization")
    }
}

fun CorotuineScope.startFlowCollection() {
    launch { collectFromCallbackFlow1() }
    launch { collectFromCallbackFlow2() } 
}
But as @Joffrey pointed out, as
startFlowCollection
actually never was actually suspending an now no longer is a suspend fun. you can also call it without an outer launch. All you need is a scope
Copy code
fun initialize() {
    <http://log.info|log.info>(TAG, "Start flow collection")
    coroutineScope.startFlowCollection(vim.value)
    <http://log.info|log.info>(TAG, "Done with initialization")
}