Hi, `StateFlow.collect()` is not subscribing at a...
# coroutines
r
Hi,
StateFlow.collect()
is not subscribing at all. Subscription count comes ZERO even after calling
collect()
I have a classes like this
Copy code
class DataApi {
   private val dataUpdates = MutableStateFlow<List<Data>?>(null)

   fun subscribeToDataUpdates(streamId:String, coroutineScope: CoroutineScope) {
     setupDatatCollection(streamId, coroutineScope)
     return dataUpdates
   }

   private fun setupDataCollection(streamId: String, scope:CoroutineScope) {
     scope.launch {
       val stream = getStream(streamId) // this is suspend
       merge(fetchInitialData(), registerDataUpdates(scope)).collectLatest { list ->
          // TILL HERE I AM GETTING THE UPDATES
          dataUpdates.emit(list) // THIS DOES NOT WORK. SUBSCRIPTION COUNT COMES 0
       }
     }
   }
}
Copy code
class DataViewModel(private val dataApi): ViewModel() {
 
    fun  loadData(streamid: String) {
       viewModelScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
           dataApi.subscribeToDataUpdates(streamid, viewModelScope).collectLatest {
               // I DO NOT GET UPDATES HERE EMITTED BY `dataUpdates`
           }
       }
    }

}
I am not sure why the subscription count is ZERO even after calling collect() In what scenario subscription count becomes ZERO. Can someone help a little bit here?
✔️ 1
j
Why do you believe that the
emit()
call happens after the state flow has started being collected?
From the point of view of
DataApi
, you don't know which
scope
you receive. So you don't know whether the
launch
doing the `merge`+`collectLatest` will be executed before or after returning
dataUpdates
from
subscribeToDataUpdates
- it's technically concurrent, so open to races
The code you provided doesn't compile because it doesn't pass a scope to
subscribeToDataUpdates
, so I don't know which scope it is in real life, but if it's also
viewModelScope
then if my memory doesn't fail me, the
launch
should be immediately run, and there is a chance you emit values to the state flow before the subscription happens
r
No I am saying its emitting after stateFlow is being collected
will be executed before or after returning
dataUpdates
from
subscribeToDataUpdates
But I immediately get the initial
null
value in the client class. And after I see the list is emitted in the
merge + collectLatest
So should not I get that list emitted by stateFlow given that initial value is emitted and client has called collectLatest?
The code you provided doesn't compile because it doesn't pass a scope
Sorry I missed it here, but it actually does. Let me correct it
then if my memory doesn't fail me, the
launch
should be immediately run, and there is a chance you emit values to the state flow before the subscription happens
I did not get this one. Could you explain this a bit more?
j
I did not get this one. Could you explain this a bit more?
I meant that AFAIR
viewModelScope
starts coroutines in an undispatched manner, so a
viewModelScope.launch
immediately executes its body until the first suspension point, as opposed to just "queuing" the body to be executed later. This means in theory the
scope.launch
in
setupDataCollection
could be executed immediately, and thus before
subscribeToDataUpdates
returns
r
> there is a chance you emit values to the state flow before the subscription happens
In merge + collectLatest, there is function
registerDataUpdates()
that actually receive the updates from server and update the list Even that update is getting emitted inside the
merge + collectLatest {}
but
dataUpdates(stateflow)
does not emit. By this point it should be subscribed right as this server update happens way later?
as opposed to just "queuing" the body to be executed later. This means in theory the
scope.launch
in
setupDataCollection
could be executed immediately, and thus before
subscribeToDataUpdates
returns
Lets say if its queuing issue. Any idea how to fix this?
This is solved. actually getter of the flow variables was always giving the new StateFlow which caused the issue