ursus
12/26/2021, 2:10 AMclass Presenter {
init {
presenterScope.launch {
syncer.syncingFinishedEvent
.collect {
navigateSomewhere()
}
}
syncer.sync()
}
}
class Syncer {
fun sync() {
syncerScope.launch {
...
syncFinishedEvent.emit(Unit)
}
}
}
Is there a way to only call syncer.sync() after syncer.syncingFinishedEvent is for sure subscribed to? I cannot miss a eventbezrukov
12/26/2021, 6:36 AMstart = Undispatched in the first launch, in this case you will subscribe to the flow in the same call frame (but in this case it will be executed on the presenter's constructor thread)Nick Allen
12/27/2021, 1:46 AMflowOn could easily break this approach. Just because you subscribed to a Flow does not mean that the upstream source Flow has been subscribed.
For this particular scenario, I don't even see the need for any Flow. If you want to do something and wait for it to finish, just make it a suspend method. suspend fun sync() would make it a lot simpler.
If you really need/want it to run in it's own scope, then return the Job so you can join it.ursus
12/27/2021, 3:11 AMdoOnSubscribe from rxJava, but not sure how to implement it as line after collect, when creating custom operator, only runs after the flow completes 😔
To be honest I find it silly that I cannot guarantee im collecting a flow, when the same component is initiating work which writes to the flow, so logically, it is present .. why are the hacks needed.. it just worked with rxjavaNick Allen
12/27/2021, 3:30 AMval syncJob = syncerScope.launch(start = LAZY) { ... }
suspend fun sync() = syncJob.join()Nick Allen
12/27/2021, 3:48 AMdoOnSubscribe would be onStart. If using a SharedFlow there is the onSubscription method.Nick Allen
12/27/2021, 3:59 AMdoOnSubscribe only really tells you that subscription is setup downstream , it does not guarantee upstream subscription. The subscribeOn operator can mess up such a use case just like flowOn with a Flow . If you want to wait for something, you should really do it explicitly. That's no different in RxJava than Kotlin coroutines.Nick Allen
12/27/2021, 4:14 AMshareIn operator and subscribers can avoid "missing" it if you use replay = 1.Nick Allen
12/27/2021, 4:21 AMval syncJob = syncerScope.async<Unit>(start = LAZY) { ... }
suspend fun sync() = syncJob.await()ursus
12/28/2021, 2:12 AMdistinctUntilChanged behavior of StateFlow
Anyways, SharedFlow.onSubscription looks like what I want, but it feels odd to expose flow subclass as public api, or its just me?Nick Allen
12/28/2021, 2:52 AMit feels odd to expose flow subclass as public api, or its just me?Yes it's weird, imo, for this use case. In general it's not weird to expose SharedFlow or StateFlow. If you describe a bit more of your use case, I'm guessing a better solution can be found.
ursus
12/28/2021, 4:30 AMSycner.syncInProgress: Flow<Boolean>
Now this usecase came where I need to navigate to some blocking ui, trigger a sync, wait for it to finish, then navigate to next screen.
My solution was, as it was driven by StateFlow, to drop(1) to get ride of the cached value, and to request the sync. Given its dropped 1, I should only receive "live" values, wait for filter { it == false}`` as my signal to navigate to next screen.
What however sometimes happens is that, if sync is fast, and collector (main thread drvien) is busy, it skips the true emit. And the when state changes back to false, its equal to the initial cached emit in eyes of the collector, so a no-emit.
And now my ui is stuck.Nick Allen
12/28/2021, 5:05 AMsyncerScope, and then `join`/`await` it.Nick Allen
12/28/2021, 5:08 AMfun sync(): SyncId could be linked to Flow<SyncId> and the monotonic nature means you can miss SyncIds in the Flow and still know they happened.Nick Allen
12/28/2021, 5:11 AMFlow<Boolean> is really "I want to know if this is syncing" which is not what you want. It's essentially the same for Flow<Unit> since there's nothing except vicinity of timing letting you guess that the event and request are related but you may not know for sure.