https://kotlinlang.org logo
Title
c

Christian Ekrem

02/01/2021, 1:16 PM
Hey! I need some help understanding something. Somewhere in my code there’s a method for fetching Something. The method should return a flow that initially emits the cached value, and then emits the “fresh” value after querying our API. Most of this is done by Room – Room emits new data whenever a given table is updated. I’ve got it working, but I tried making the code a bit simpler and that’s where both the functionality and my understanding broke 😞
// stream is _really_ someDao.loadAll(): Flow<Something>
val stream = MutableStateFlow("cachedValue")

// refresh is _really_ fetchSomethingFromApi.also { someDao.updateData(it) }
val refresh = suspend {
    delay(1000)
    stream.value = "freshValueFromAPI"
}

suspend fun whyDoesThisNotWork(): Flow<String> = stream
    .onStart {
        coroutineScope {
            launch {
                refresh()
            }
        }
    }

suspend fun thisWorks(): Flow<String> = flow {
    coroutineScope {
        launch {
            refresh()
        }

        stream.collect {
            emit(it)
        }
    }
}
It’s not like the working version is horrible, I just want to understand why the first one does not work!
for the record, this is how I tested it:
runBlocking {
    thisWorks().take(2).collect {
        println(it)
    }
}
and
runBlocking {
    whyDoesThisNotWork().take(2).collect {
        println(it)
    }
}
The working one emits what I expect, when I expect (that is “cachedValue” immediately, and then “freshValueFromAPI” after one second). The other one simply times out. If I do
.take(1)
instead, I find that the broken version only ever collects “freshValueFromAPI”