Colton Idle
04/28/2022, 3:57 PMviewModelScope.launch {
repository.getBooks().collect { list ->
state.books.clear()
state.books.addAll(list)
}
repository.doSomething() <--- Will never get called
}
I almost want to write a lint check or something to prevent myself from shooting myself in the foot. Anyone have any suggestions on how to change my mindset on this?Joffrey
04/28/2022, 4:00 PMcollect
is a function call that processes all elements of the flow. It's like a forEach
on a collection. If the collection (flow) is infinite, then forEach
(collect
) never endsColton Idle
04/28/2022, 4:01 PMgetBooksInfinite
because of this.Joffrey
04/28/2022, 4:02 PMcollect
call will process all elements in the flow (unless the current coroutine is cancelled before that of course), so it's still important to note regardless of the size of the flow.
That said, the naming indeed could be better here because you don't know whether it gets the current collection of books or if it subscribes to books indefinitely, getting new ones as they come.Joffrey
04/28/2022, 4:04 PMgetBooks()
a suspend function that returns a list of books, but rather booksFlow()
or booksSubscription()
a non-suspending function that returns a (likely infinite) flow of such itemsColton Idle
04/28/2022, 4:09 PMsuspend fun getBooks(): Flow<List<Book>>
can just be
fun getBooks(): Flow<List<Book>>
Colton Idle
04/28/2022, 4:11 PMJoffrey
04/28/2022, 4:12 PMsuspend
. The body of the flow { .. }
builders is only run when collected, that's why collect
itself has to be suspend, but not the flow builder call.
wonder why there wasn't any errorIt's not an error to mark a function as
suspend
needlessly, but the compiler should have given you a warning that the suspend
keyword was unnecessary (unless maybe it's via an interface, in which case it cannot know that some implementations wouldn't want to be suspend)Colton Idle
04/28/2022, 4:26 PMviewModelScope.launch {
repository.getBooks().collect { list ->
state.books.clear()
state.books.addAll(list)
}
}
viewModelScope.launch {
repository.doSomething()
}
Nino
04/28/2022, 4:50 PMJoffrey
04/28/2022, 4:53 PMdoSomething()
outside of the first launch (if you're already in a coroutine).
If you want to express concurrency between these 2 things, you have to put them in separate coroutines, regardless of the flow's size/finiteness. Statements that are sequential within the same coroutine (the same launch
) are executed sequentially.ephemient
04/28/2022, 6:32 PM.onEach.launchIn
doesn't require as much nesting as launch { .collect }
)
repository.getBooks().onEach { list ->
state.books = list
}.launchIn(viewModelScope)
viewModelScope.launch {
repository.doSomething()
}
Paul Woitaschek
04/28/2022, 7:25 PMPaul Woitaschek
04/28/2022, 7:25 PMmyanmarking
04/28/2022, 8:18 PMTolriq
04/29/2022, 8:25 AMinline fun <T> Flow<T>.eachInScope(scope: CoroutineScope, crossinline block: suspend (T) -> Unit): Job = onEach {
block(it)
}.launchIn(scope)
This is explicit and properly return a Job that you can keep if you need cancellation without cancelling the scope.Stylianos Gakis
04/29/2022, 12:14 PMviewModelScope.launch {
launch {
repository.getBooks().collect { list ->
state.books.clear()
state.books.addAll(list)
}
}
repository.doSomething()
}
Just my personal opinion on this 😅Colton Idle
04/29/2022, 3:39 PMjulian
04/30/2022, 7:41 PMColton Idle
05/03/2022, 1:05 AMjulian
05/03/2022, 1:30 AMlaunch {
launch {
flow.collect { }
}
someSuspendFn()
}
Note that collection is the last thing to execute in the inner coroutine. But the inner coroutine does not need to be the last to execute in the outer coroutine, since launch
is concurrent relative to code following it, not sequential.
It's okay to catch yourself making the same mistake over and over. That's how learning occurs naturally. Unlearning a habit isn't easy. You fail, fail, fail, but each time remind yourself of your goal to not fail. Over time, you fail less frequently, and then not at all.Stylianos Gakis
05/03/2022, 6:53 AMPaul Woitaschek
05/03/2022, 6:58 AMStylianos Gakis
05/03/2022, 7:15 AMPaul Woitaschek
05/03/2022, 7:17 AMPaul Woitaschek
05/03/2022, 7:18 AMsuspend fun <T> Flow<T>.infiniteCollect(collector: FlowCollector<T>): Nothing {
collect(collector)
awaitCancellation()
}
This one will do it even betterephemient
05/03/2022, 7:31 AMPaul Woitaschek
05/03/2022, 7:46 AMephemient
05/03/2022, 7:55 AMawaitCancellation()
does change behavior - the containing scope will never end unless cancelledPaul Woitaschek
05/03/2022, 8:37 AMursus
06/20/2022, 10:32 PM