Guillaume Fleury
04/24/2023, 6:00 AMsuspend fun getFlow(): Flow<Int> {
return flow {
emit(5)
throw Exception("some exception")
}
}
I am collecting the flow somewhere else.,,
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.CLOVIS
04/24/2023, 4:23 PMflow
should very rarely be suspend
. The flow encapsulates suspend
between elements already, it is redundant to also allow suspend
at the function level:
fun getFlow() = flow {
emit(5)
throw Exception("some exception")
}
CLOVIS
04/24/2023, 4:26 PMsharedIn
, stateIn
, buffer
, implémentations such as SharedFlow
or StateFlow
, Compose's collectAsState
, will cause the behavior you're seeing (some values are skipped)CLOVIS
04/24/2023, 4:28 PMValue collected: 5
Exception caught: java.lang.Exception: some error
CLOVIS
04/24/2023, 4:31 PMGuillaume Fleury
04/25/2023, 7:10 AMfun 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.
Guillaume Fleury
04/25/2023, 7:25 AM```// 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
}
}
}```