Giorgi
03/06/2023, 6:18 PMSharingStarted.WhileSubscribed()
, Im on JS target, with compose multiplatform.
lets say I have a code like this:
class HistoryScreenViewModel : DIAware {
override val di: DI = kodein
private val dispatcher by instance<CoroutineDispatcher>(tag = MAIN)
private val viewModelScope = CoroutineScope(dispatcher)
private val historyFilesRepository by instance<HistoryFilesRepository>()
private val settingsRepo by instance<SettingsRepo>()
val markdownText = historyFilesRepository.selectedFiles
.combine(settingsRepo.minimumAmountOfVideoClicks, ::YoutubeHistoryParams)
.map(::parseHistoryToMarkdown)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
.onSubscription { println("-- subscribed") }
.onCompletion { println("-- onCompletion") }
}
There is a markdownText
StateFlow being created with WhileSubscribed
argument. My expectation is that when markdownText
is subscribed, then it will execute all functions above, including parseHistoryToMarkdown
, then when I switch to another screen it will complete (it does print "-- onCompletion"
in the console) and keep the cached value. Later, when I go back to same screen, it will emit cached value or if settingsRepo.minimumAmountOfVideoClicks
changed, then it will recalculate it.
But it happens differently. When I switch back to the screen and collect is started then it will execute parseHistoryToMarkdown
function once again.
Should not it emit same value if settingsRepo.minimumAmountOfVideoClicks
did not change? The documentation says that it keeps replay cache forever by defaultKevin Worth
03/06/2023, 8:03 PMviewModelScope
, it appears that changing screens cancels that scope and restarts it when you return to that screen. Have you tried another scope? But also, how crucial is it to run only once? If it’s not very very costly, then you’re probably fine having it stop and start the way it is currently.Giorgi
03/08/2023, 12:07 PMparseHistoryToMarkdown
function takes 4-5 seconds (it parses 5mb JSON text, sorts them and generates markdown text), so thats why I want it to happen only once.
I got closer to the problem now. it was not about the scope, but the state flow. So in my original code historyFilesRepository.selectedFiles
is a StateFlow, so every time markdownText
was subscribed it would emit the value anyway.
okay. Here is my small failing test
@OptIn(ExperimentalCoroutinesApi::class)
class Learning {
@Test
fun someRandomLearningTest() = runTest {
val viewModelScope = CoroutineScope(Dispatchers.Default)
var numberOfMapExecution = 0
val data = MutableStateFlow(1)
.map {
println("Doing some heavy calculations")
numberOfMapExecution += 1
it + 5
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
.onSubscription { println("-- subscribed") }
.onCompletion { println("-- onCompletion") }
println(data.take(2).first())
assertEquals(6, data.first())
assertEquals(6, data.first())
assertEquals(1, numberOfMapExecution)
}
}
what would you recommend here? I dont even know if it wil lbe possible to achieve this while having MutableStateFlows. All I want is .map
to not run too many timesstateIn
function to not resubscribe to above flows?