Hello Kotlin experts, I'm a Kotlin/Flow beginner a...
# flow
g
Hello Kotlin experts, I'm a Kotlin/Flow beginner and I'm trying to create a flow and then collect it but I am not getting the results I am expecting. I'm creating a flow where errors(exceptions) can be thrown.
Copy code
suspend fun getFlow(): Flow<Int> {
    return flow {
        emit(5)
        throw Exception("some exception")
    }
}
I am collecting the flow somewhere else.,,
Copy code
launch {
    getFlow()
        .catch { throwable -> 
            // handle exception
        }
        .collect {
            // do someting
        }
}
The problem is that when a value is emitted and an exception thrown immediately after, the value is lost/not collected. It is possible to have the exception caught/flow cancelled only after all emitted values have been collected? Thanks.
c
First, note that a function returning a
flow
should very rarely be
suspend
. The flow encapsulates
suspend
between elements already, it is redundant to also allow
suspend
at the function level:
Copy code
fun getFlow() = flow {
    emit(5)
    throw Exception("some exception")
}
What kind of flow are you using? Literally like this example? Operators like
sharedIn
,
stateIn
,
buffer
, implémentations such as
SharedFlow
or
StateFlow
, Compose's
collectAsState
, will cause the behavior you're seeing (some values are skipped)
Your example, as you wrote it, does print the value before the exception, so I think there's something else in your code that causes what you're seeing. https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS44LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncy[…]sgcHJpbnRsbihcIlZhbHVlIGNvbGxlY3RlZDogJGl0XCIpIH1cbn0ifQ==
Copy code
Value collected: 5
Exception caught: java.lang.Exception: some error
^ if you can manage to recreate your situation by editing the above link (in the playground), it will be much easier to explain what the problem is
g
@CLOVIS Thank you very much for the reply and sorry for the lack of details. I have made some modifications and I think I've located the source of the problem. I cannot share the complete code but let me share some pseudo-code.
fun getFlow(): Flow<Data> = flow {
// fetch data from Room DB
emit(roomData)
// fetch data from web/remote
// networking error throws an exception
emit(remoteData)
}
```// cancel previous jobs
cancellableScope.coroutineContext.cancelChildren()
cancellableScope.launch {
// this emits once(remote), maybe twice(local + remote)
getFlow()
.flowOn(Dispatchers.IO) // when I comment this, value is collected
.catch { throwable ->
// handle
}
.collect {
// do something with value
}
}```
It seems that flowOn is the cause of the problem. When I comment it, the roomData is collected and then the exception is caught. However, when I update the playground above to run on Dispatchers.IO, it still works(value is collected THEN exception is caught). Is it possible that emitting values in a different dispatcher causes values to be lost? Thanks.
@CLOVIS This seems to work, does that make sense?
```// scope must be Dispatcher.IO
private val cancellableScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
fun getData() {
// cancel previous jobs
cancellableScope.coroutineContext.cancelChildren()
cancellableScope.launch {
getFlow()
.catch { throwable ->
viewModelScope.launch {
// handle
}
}
.collect {
viewModelScope.launch {
// do something with value
}
}
}```