-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Flow.collectLatest #1269
Comments
Possible Implementation I think: suspend inline fun <T> Flow<T>.collectLatest(crossinline action: suspend (value: T) -> Unit) {
var job: Job? = null
val scope = CoroutineScope(coroutineContext)
this.collect { value: T ->
job?.cancelAndJoin()
job = scope.launch { action(value) }
}
job?.join()
} |
I have used such a thing for a long time (also have a version for channels), with a longer name, and the ability to skip equals: I need it a lot of cases, so I'd vote for it coming here. |
What's the use-case? I get the example of usage, but where would you actually use it? |
@LouisCAD For example: val flow = flowOf(1, 2, 2, 3).delayEach(100)
flow.distinctUntilChanged().collectLatest {
// Do something
} |
@BoxResin Yes, but it's a little less efficient, and may force an additional indentation level of the lambda. |
@elizarov Here's an example: https://github.com/LouisCAD/Splitties/blob/develop/samples/android-app/src/androidMain/kotlin/com/example/splitties/main/MainActivity.kt#L68-L90 Can also be helpful if you want to have a loop running while in a certain state that is emitted from the flow. |
@elizarov Here's another example: object Config {
val school: Flow<School?> = ...
}
class DailyMealViewModel(val date: TimePoint) : CoroutineScope {
private val job = SupervisorJob()
override val context = Dispatchers.Main + job
val content = MutableLiveData<DailyMealViewContent>()
init {
// `this.launch { ... }` would be canceled when `this.onCleared()` is called.
this.launch {
Config.school.collectLatest { school: School? ->
// This function will be canceled when a new school emitted.
this.onSchoolChanged(school)
}
}
}
private suspend fun onSchoolChanged(school: School?) {
if (school == null) {
this.content.value = DailyMealViewContent.Error(
message = "Please select school",
button = "Select" to this::onClickSchoolSetting
)
return
}
MealDatabase.observe(school.code, this.date).collectLatest { dailyMeal: DailyMeal? ->
// This function will be canceled when the `dailyMeal` in database changed.
this.onDailyMealChanged(dailyMeal)
}
}
private suspend fun onDailyMealChanged(dailyMeal: DailyMeal?) {
if (dailyMeal == null) {
this.content.value = DailyMealViewContent.Error(
message = "Please download meal data",
button = "Download" to this::onClickDownloadMeal
)
return
}
// Something to deal with `dailyMeal`
...
}
override fun onCleared() {
this.job.cancel()
}
} |
We've recently added There:
prints |
Another observise, the requested behavior for |
@elizarov Yes, I considered suspend fun main() {
val flow = flowOf(1, 2, 3, 4, 5)
flow.switchMap { it -> flowOf(it) }.collect { value: Int ->
delay(100)
println(value) // So only '5' will be printed.
}
} I just want to take the 'switching' behavior of |
Thanks. That is an interesting observation and it raises the whole set of issues with respect to naming. I wonder if we've making a mistaking of providing |
@elizarov val accountFlow: Flow<Account?> // null value means that user has signed out.
class Account {
val emailFlow: Flow<String>
val nicknameFlow: Flow<String>
val phoneNumberFlow: Flow<String>
}
// Suppose this code is in a suspend lambda.
accountFlow.collectLatest { account: Account? ->
if (account != null) {
showAccountUI()
// This lambda should be canceled when a new account is emitted.
account.emailFlow.collect { email: String -> /* Redraw email UI */ }
// This lambda should be canceled when a new account is emitted.
account.nicknameFlow.collect { nickname: String -> /* Redraw nickname UI */ }
// This lambda should be canceled when a new account is emitted.
account.phoneNumberFlow.collect { phoneNumber: String -> /* Redraw phone number UI */ }
} else {
showEmptyAccountUI()
}
} The |
@BoxResin please note that Flow (as opposed to Rx) supports nulls, so using switchMap is still possible:
|
Function signature
Behavior
The text was updated successfully, but these errors were encountered: